mirror of
https://github.com/status-im/nim-confutils.git
synced 2025-01-19 08:32:31 +00:00
winreg encoder-decoder implementation
This commit is contained in:
parent
0df747294e
commit
4304251b09
@ -7,6 +7,7 @@ type
|
||||
WinregReader* = object
|
||||
hKey: HKEY
|
||||
path: string
|
||||
key: seq[string]
|
||||
|
||||
WinregReaderError* = object of WinregError
|
||||
|
||||
@ -29,23 +30,54 @@ proc init*(T: type WinregReader,
|
||||
result.hKey = hKey
|
||||
result.path = path
|
||||
|
||||
template getUnderlyingType*[T](_: Option[T]): untyped = T
|
||||
|
||||
proc readValue*[T](r: var WinregReader, value: var T)
|
||||
{.raises: [SerializationError, IOError, Defect].} =
|
||||
mixin readValue
|
||||
# TODO: reduce allocation
|
||||
|
||||
when T is (SomePrimitives or range):
|
||||
getValue(w.hKey, w.path, w.key, value)
|
||||
when T is (SomePrimitives or range or string):
|
||||
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):
|
||||
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):
|
||||
# TODO: reduce allocation
|
||||
discard
|
||||
let key = r.key[^1]
|
||||
for i in 0..<value.len:
|
||||
r.key[^1] = key & $i
|
||||
r.readValue(value[i])
|
||||
else:
|
||||
const typeName = typetraits.name(T)
|
||||
{.fatal: "Failed to convert from Winreg array an unsupported type: " & typeName.}
|
||||
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:
|
||||
const typeName = typetraits.name(T)
|
||||
{.fatal: "Failed to convert from Winreg an unsupported type: " & typeName.}
|
||||
|
@ -150,3 +150,17 @@ template uTypeIsRecord*[N, T](_: type array[N, T]): bool =
|
||||
true
|
||||
else:
|
||||
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)
|
||||
|
||||
template encode*(_: type Winreg,
|
||||
value: auto,
|
||||
hKey: HKEY,
|
||||
path: string,
|
||||
value: auto,
|
||||
params: varargs[untyped]) =
|
||||
mixin init, WriterType, writeValue
|
||||
|
||||
@ -42,6 +42,9 @@ template loadFile*(_: type Winreg,
|
||||
params: varargs[untyped]): auto =
|
||||
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)
|
||||
var reader = unpackArgs(init, [WinregReader, hKey, path, params])
|
||||
reader.readValue(RecordType)
|
||||
@ -49,6 +52,7 @@ template loadFile*(_: type Winreg,
|
||||
template saveFile*(Format: type, filename: string, value: auto, params: varargs[untyped]) =
|
||||
mixin init, WriterType, writeValue
|
||||
|
||||
# filename should be a Windows Registry path
|
||||
let (hKey, path) = parseWinregPath(filename)
|
||||
var writer = unpackArgs(init, [WinregWriter, hKey, path, params])
|
||||
writer.writeValue(value)
|
||||
|
@ -7,7 +7,7 @@ type
|
||||
WinregWriter* = object
|
||||
hKey: HKEY
|
||||
path: string
|
||||
key: string
|
||||
key: seq[string]
|
||||
|
||||
proc init*(T: type WinregWriter,
|
||||
hKey: HKEY, path: string): T =
|
||||
@ -16,20 +16,34 @@ proc init*(T: type WinregWriter,
|
||||
|
||||
proc writeValue*(w: var WinregWriter, value: auto) =
|
||||
mixin enumInstanceSerializedFields, writeValue, writeFieldIMPL
|
||||
# TODO: reduce allocation
|
||||
|
||||
when value is (SomePrimitives or range):
|
||||
setValue(w.hKey, w.path, w.key, value)
|
||||
when value is (SomePrimitives or range or string):
|
||||
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):
|
||||
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):
|
||||
# TODO: reduce allocation
|
||||
discard
|
||||
let key = w.key[^1]
|
||||
for i in 0..<value.len:
|
||||
w.key[^1] = key & $i
|
||||
w.writeValue(value[i])
|
||||
else:
|
||||
const typeName = typetraits.name(value.type)
|
||||
{.fatal: "Failed to convert to Winreg array an unsupported type: " & typeName.}
|
||||
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:
|
||||
const typeName = typetraits.name(value.type)
|
||||
{.fatal: "Failed to convert to Winreg an unsupported type: " & typeName.}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import
|
||||
unittest,
|
||||
unittest, options,
|
||||
../confutils/winreg/winreg_serialization
|
||||
|
||||
type
|
||||
@ -18,7 +18,7 @@ template readWrite(key: string, val: typed) =
|
||||
check ok == true
|
||||
check outVal == val
|
||||
|
||||
proc testWinregUtils() =
|
||||
proc testUtils() =
|
||||
suite "winreg utils test suite":
|
||||
readWrite("some number", 123'u32)
|
||||
readWrite("some number 64", 123'u64)
|
||||
@ -36,4 +36,63 @@ proc testWinregUtils() =
|
||||
check hKey == HKCR
|
||||
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