mirror of
https://github.com/status-im/nim-confutils.git
synced 2025-01-31 14:26:39 +00:00
winreg encoder-decoder implementation
This commit is contained in:
parent
0df747294e
commit
4304251b09
@ -7,6 +7,7 @@ type
|
|||||||
WinregReader* = object
|
WinregReader* = object
|
||||||
hKey: HKEY
|
hKey: HKEY
|
||||||
path: string
|
path: string
|
||||||
|
key: seq[string]
|
||||||
|
|
||||||
WinregReaderError* = object of WinregError
|
WinregReaderError* = object of WinregError
|
||||||
|
|
||||||
@ -29,23 +30,54 @@ proc init*(T: type WinregReader,
|
|||||||
result.hKey = hKey
|
result.hKey = hKey
|
||||||
result.path = path
|
result.path = path
|
||||||
|
|
||||||
|
template getUnderlyingType*[T](_: Option[T]): untyped = T
|
||||||
|
|
||||||
proc readValue*[T](r: var WinregReader, value: var T)
|
proc readValue*[T](r: var WinregReader, value: var T)
|
||||||
{.raises: [SerializationError, IOError, Defect].} =
|
{.raises: [SerializationError, IOError, Defect].} =
|
||||||
mixin readValue
|
mixin readValue
|
||||||
|
# TODO: reduce allocation
|
||||||
|
|
||||||
when T is (SomePrimitives or range):
|
when T is (SomePrimitives or range or string):
|
||||||
getValue(w.hKey, w.path, w.key, value)
|
let path = constructPath(r.path, r.key)
|
||||||
|
discard getValue(r.hKey, path, r.key[^1], value)
|
||||||
|
elif T is Option:
|
||||||
|
let path = constructPath(r.path, r.key)
|
||||||
|
var outVal: getUnderlyingType(value)
|
||||||
|
if getValue(r.hKey, path, r.key[^1], outVal):
|
||||||
|
value = some(outVal)
|
||||||
elif T is (seq or array):
|
elif T is (seq or array):
|
||||||
when uTypeIsPrimitives(T):
|
when uTypeIsPrimitives(T):
|
||||||
getValue(w.hKey, w.path, w.key, value)
|
let path = constructPath(r.path, r.key)
|
||||||
|
discard getValue(r.hKey, path, r.key[^1], value)
|
||||||
elif uTypeIsRecord(T):
|
elif uTypeIsRecord(T):
|
||||||
# TODO: reduce allocation
|
let key = r.key[^1]
|
||||||
discard
|
for i in 0..<value.len:
|
||||||
|
r.key[^1] = key & $i
|
||||||
|
r.readValue(value[i])
|
||||||
else:
|
else:
|
||||||
const typeName = typetraits.name(T)
|
const typeName = typetraits.name(T)
|
||||||
{.fatal: "Failed to convert from Winreg array an unsupported type: " & typeName.}
|
{.fatal: "Failed to convert from Winreg array an unsupported type: " & typeName.}
|
||||||
elif T is (object or tuple):
|
elif T is (object or tuple):
|
||||||
discard
|
type T = type(value)
|
||||||
|
|
||||||
|
when T.totalSerializedFields > 0:
|
||||||
|
let fields = T.fieldReadersTable(WinregReader)
|
||||||
|
var expectedFieldPos = 0
|
||||||
|
r.key.add ""
|
||||||
|
value.enumInstanceSerializedFields(fieldName, field):
|
||||||
|
type FieldType = type field
|
||||||
|
|
||||||
|
when T is tuple:
|
||||||
|
r.key[^1] = $expectedFieldPos
|
||||||
|
var reader = fields[][expectedFieldPos].reader
|
||||||
|
expectedFieldPos += 1
|
||||||
|
else:
|
||||||
|
r.key[^1] = fieldName
|
||||||
|
var reader = findFieldReader(fields[], fieldName, expectedFieldPos)
|
||||||
|
|
||||||
|
if reader != nil:
|
||||||
|
reader(value, r)
|
||||||
|
discard r.key.pop()
|
||||||
else:
|
else:
|
||||||
const typeName = typetraits.name(T)
|
const typeName = typetraits.name(T)
|
||||||
{.fatal: "Failed to convert from Winreg an unsupported type: " & typeName.}
|
{.fatal: "Failed to convert from Winreg an unsupported type: " & typeName.}
|
||||||
|
@ -150,3 +150,17 @@ template uTypeIsRecord*[N, T](_: type array[N, T]): bool =
|
|||||||
true
|
true
|
||||||
else:
|
else:
|
||||||
false
|
false
|
||||||
|
|
||||||
|
func constructPath*(root: string, keys: openArray[string]): string =
|
||||||
|
if keys.len <= 1:
|
||||||
|
return root
|
||||||
|
var size = root.len + 1
|
||||||
|
for i in 0..<keys.len-1:
|
||||||
|
inc(size, keys[i].len + 1)
|
||||||
|
result = newStringOfCap(size)
|
||||||
|
result.add root
|
||||||
|
result. add '\\'
|
||||||
|
for i in 0..<keys.len-1:
|
||||||
|
result.add keys[i]
|
||||||
|
if i < keys.len-2:
|
||||||
|
result. add '\\'
|
||||||
|
@ -26,9 +26,9 @@ template decode*(_: type Winreg,
|
|||||||
reader.readValue(RecordType)
|
reader.readValue(RecordType)
|
||||||
|
|
||||||
template encode*(_: type Winreg,
|
template encode*(_: type Winreg,
|
||||||
value: auto,
|
|
||||||
hKey: HKEY,
|
hKey: HKEY,
|
||||||
path: string,
|
path: string,
|
||||||
|
value: auto,
|
||||||
params: varargs[untyped]) =
|
params: varargs[untyped]) =
|
||||||
mixin init, WriterType, writeValue
|
mixin init, WriterType, writeValue
|
||||||
|
|
||||||
@ -42,6 +42,9 @@ template loadFile*(_: type Winreg,
|
|||||||
params: varargs[untyped]): auto =
|
params: varargs[untyped]): auto =
|
||||||
mixin init, ReaderType, readValue
|
mixin init, ReaderType, readValue
|
||||||
|
|
||||||
|
# filename should be a Windows Registry path
|
||||||
|
# such as "HKEY_CLASSES_ROOT\\SOFTWARE\\Nimbus"
|
||||||
|
# or "HKCU\\SOFTWARE\\Nimbus"
|
||||||
let (hKey, path) = parseWinregPath(filename)
|
let (hKey, path) = parseWinregPath(filename)
|
||||||
var reader = unpackArgs(init, [WinregReader, hKey, path, params])
|
var reader = unpackArgs(init, [WinregReader, hKey, path, params])
|
||||||
reader.readValue(RecordType)
|
reader.readValue(RecordType)
|
||||||
@ -49,6 +52,7 @@ template loadFile*(_: type Winreg,
|
|||||||
template saveFile*(Format: type, filename: string, value: auto, params: varargs[untyped]) =
|
template saveFile*(Format: type, filename: string, value: auto, params: varargs[untyped]) =
|
||||||
mixin init, WriterType, writeValue
|
mixin init, WriterType, writeValue
|
||||||
|
|
||||||
|
# filename should be a Windows Registry path
|
||||||
let (hKey, path) = parseWinregPath(filename)
|
let (hKey, path) = parseWinregPath(filename)
|
||||||
var writer = unpackArgs(init, [WinregWriter, hKey, path, params])
|
var writer = unpackArgs(init, [WinregWriter, hKey, path, params])
|
||||||
writer.writeValue(value)
|
writer.writeValue(value)
|
||||||
|
@ -7,7 +7,7 @@ type
|
|||||||
WinregWriter* = object
|
WinregWriter* = object
|
||||||
hKey: HKEY
|
hKey: HKEY
|
||||||
path: string
|
path: string
|
||||||
key: string
|
key: seq[string]
|
||||||
|
|
||||||
proc init*(T: type WinregWriter,
|
proc init*(T: type WinregWriter,
|
||||||
hKey: HKEY, path: string): T =
|
hKey: HKEY, path: string): T =
|
||||||
@ -16,20 +16,34 @@ proc init*(T: type WinregWriter,
|
|||||||
|
|
||||||
proc writeValue*(w: var WinregWriter, value: auto) =
|
proc writeValue*(w: var WinregWriter, value: auto) =
|
||||||
mixin enumInstanceSerializedFields, writeValue, writeFieldIMPL
|
mixin enumInstanceSerializedFields, writeValue, writeFieldIMPL
|
||||||
|
# TODO: reduce allocation
|
||||||
|
|
||||||
when value is (SomePrimitives or range):
|
when value is (SomePrimitives or range or string):
|
||||||
setValue(w.hKey, w.path, w.key, value)
|
let path = constructPath(w.path, w.key)
|
||||||
|
discard setValue(w.hKey, path, w.key[^1], value)
|
||||||
|
elif value is Option:
|
||||||
|
if value.isSome:
|
||||||
|
w.writeValue value.get
|
||||||
elif value is (seq or array or openArray):
|
elif value is (seq or array or openArray):
|
||||||
when uTypeIsPrimitives(type value):
|
when uTypeIsPrimitives(type value):
|
||||||
setValue(w.hKey, w.path, w.key, value)
|
let path = constructPath(w.path, w.key)
|
||||||
|
discard setValue(w.hKey, path, w.key[^1], value)
|
||||||
elif uTypeIsRecord(type value):
|
elif uTypeIsRecord(type value):
|
||||||
# TODO: reduce allocation
|
let key = w.key[^1]
|
||||||
discard
|
for i in 0..<value.len:
|
||||||
|
w.key[^1] = key & $i
|
||||||
|
w.writeValue(value[i])
|
||||||
else:
|
else:
|
||||||
const typeName = typetraits.name(value.type)
|
const typeName = typetraits.name(value.type)
|
||||||
{.fatal: "Failed to convert to Winreg array an unsupported type: " & typeName.}
|
{.fatal: "Failed to convert to Winreg array an unsupported type: " & typeName.}
|
||||||
elif value is (object or tuple):
|
elif value is (object or tuple):
|
||||||
discard
|
type RecordType = type value
|
||||||
|
w.key.add ""
|
||||||
|
value.enumInstanceSerializedFields(fieldName, field):
|
||||||
|
type FieldType = type field
|
||||||
|
w.key[^1] = fieldName
|
||||||
|
w.writeFieldIMPL(FieldTag[RecordType, fieldName, FieldType], field, value)
|
||||||
|
discard w.key.pop()
|
||||||
else:
|
else:
|
||||||
const typeName = typetraits.name(value.type)
|
const typeName = typetraits.name(value.type)
|
||||||
{.fatal: "Failed to convert to Winreg an unsupported type: " & typeName.}
|
{.fatal: "Failed to convert to Winreg an unsupported type: " & typeName.}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import
|
import
|
||||||
unittest,
|
unittest, options,
|
||||||
../confutils/winreg/winreg_serialization
|
../confutils/winreg/winreg_serialization
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -18,7 +18,7 @@ template readWrite(key: string, val: typed) =
|
|||||||
check ok == true
|
check ok == true
|
||||||
check outVal == val
|
check outVal == val
|
||||||
|
|
||||||
proc testWinregUtils() =
|
proc testUtils() =
|
||||||
suite "winreg utils test suite":
|
suite "winreg utils test suite":
|
||||||
readWrite("some number", 123'u32)
|
readWrite("some number", 123'u32)
|
||||||
readWrite("some number 64", 123'u64)
|
readWrite("some number 64", 123'u64)
|
||||||
@ -36,4 +36,63 @@ proc testWinregUtils() =
|
|||||||
check hKey == HKCR
|
check hKey == HKCR
|
||||||
check path == commonPath
|
check path == commonPath
|
||||||
|
|
||||||
testWinregUtils()
|
proc testEncoder() =
|
||||||
|
type
|
||||||
|
Class = enum
|
||||||
|
Truck
|
||||||
|
MPV
|
||||||
|
SUV
|
||||||
|
|
||||||
|
Fuel = enum
|
||||||
|
Gasoline
|
||||||
|
Diesel
|
||||||
|
|
||||||
|
Engine = object
|
||||||
|
cylinder: int
|
||||||
|
valve: int16
|
||||||
|
fuel: Fuel
|
||||||
|
|
||||||
|
Suspension = object
|
||||||
|
dist: int
|
||||||
|
length: int
|
||||||
|
|
||||||
|
Vehicle = object
|
||||||
|
name: string
|
||||||
|
color: int
|
||||||
|
class: Class
|
||||||
|
engine: Engine
|
||||||
|
wheel: int
|
||||||
|
suspension: array[3, Suspension]
|
||||||
|
door: array[4, int]
|
||||||
|
antennae: Option[int]
|
||||||
|
bumper: Option[string]
|
||||||
|
|
||||||
|
suite "winreg encoder test suite":
|
||||||
|
test "basic encoder and decoder":
|
||||||
|
let v = Vehicle(
|
||||||
|
name: "buggy",
|
||||||
|
color: 213,
|
||||||
|
class: MPV,
|
||||||
|
engine: Engine(
|
||||||
|
cylinder: 3,
|
||||||
|
valve: 2,
|
||||||
|
fuel: Diesel
|
||||||
|
),
|
||||||
|
wheel: 6,
|
||||||
|
door: [1,2,3,4],
|
||||||
|
suspension: [
|
||||||
|
Suspension(dist: 1, length: 5),
|
||||||
|
Suspension(dist: 2, length: 6),
|
||||||
|
Suspension(dist: 3, length: 7)
|
||||||
|
],
|
||||||
|
bumper: some("Chromium")
|
||||||
|
)
|
||||||
|
|
||||||
|
Winreg.encode(HKCU, commonPath, v)
|
||||||
|
let x = Winreg.decode(HKCU, commonPath, Vehicle)
|
||||||
|
check x == v
|
||||||
|
check x.antennae.isNone
|
||||||
|
check x.bumper.get() == "Chromium"
|
||||||
|
|
||||||
|
testUtils()
|
||||||
|
testEncoder()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user