winreg encoder-decoder implementation

This commit is contained in:
jangko 2020-10-21 16:07:25 +07:00 committed by zah
parent 0df747294e
commit 4304251b09
5 changed files with 140 additions and 17 deletions

View File

@ -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.}

View File

@ -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 '\\'

View File

@ -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)

View File

@ -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.}

View File

@ -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()