Option support in toPreserves and fromPreserves

This commit is contained in:
Emery Hemingway 2024-01-03 14:14:46 +02:00
parent 501d6cc012
commit 0acd369262
3 changed files with 37 additions and 22 deletions

View File

@ -1,6 +1,6 @@
# Package # Package
version = "20240102" version = "20240103"
author = "Emery Hemingway" author = "Emery Hemingway"
description = "data model and serialization format" description = "data model and serialization format"
license = "Unlicense" license = "Unlicense"

View File

@ -142,15 +142,6 @@ proc `&`*(x: Value; y: seq[Value]): Value =
raise newException(ValueError, "cannot concatenate to non-sequence value") raise newException(ValueError, "cannot concatenate to non-sequence value")
result = Value(kind: pkSequence, sequence: x.sequence & y) result = Value(kind: pkSequence, sequence: x.sequence & y)
proc getOrDefault(pr, key: Value): Value =
## Retrieves the value of `pr[key]` if `pr` is a dictionary containing `key`
## or returns the `#f` Preserves value.
if pr.kind == pkDictionary:
for (k, v) in pr.dict:
if key == k:
result = v
break
proc pop*(pr: var Value; key: Value; val: var Value): bool = proc pop*(pr: var Value; key: Value; val: var Value): bool =
## Deletes the `key` from a Preserves dictionary. ## Deletes the `key` from a Preserves dictionary.
## Returns true, if the key existed, and sets `val` to the mapping ## Returns true, if the key existed, and sets `val` to the mapping
@ -440,9 +431,15 @@ proc toPreserves*[T](x: T): Value {.gcsafe.} =
elif T.hasCustomPragma(preservesDictionary): elif T.hasCustomPragma(preservesDictionary):
result = initDictionary() result = initDictionary()
for key, val in x.fieldPairs: for key, val in x.fieldPairs:
var pr = fieldToPreserve(key, val) when val is Option:
applyEmbed(key, pr) if val.isSome:
result[toSymbol(key)] = pr var pr = fieldToPreserve(key, val.get)
applyEmbed(key, pr)
result[key.toSymbol] = pr
else:
var pr = fieldToPreserve(key, val)
applyEmbed(key, pr)
result[key.toSymbol] = pr
sortDict(result) sortDict(result)
else: else:
{.warning: "failed to preserve object " & $T .} {.warning: "failed to preserve object " & $T .}
@ -491,6 +488,9 @@ proc toPreservesHook*[A, B](table: Table[A, B]|TableRef[A, B]): Value =
result[toPreserves(k)] = toPreserves(v) result[toPreserves(k)] = toPreserves(v)
sortDict(result) sortDict(result)
proc toPreservesHook*(o: Option): Value =
o.get.toPreserves
proc fromAtom*[T](v: var T; a: ATom): bool = proc fromAtom*[T](v: var T; a: ATom): bool =
if T is Atom: if T is Atom:
v = a v = a
@ -715,12 +715,17 @@ proc fromPreserves*[T](v: var T; pr: Value): bool {.gcsafe.} =
if pr.isDictionary: if pr.isDictionary:
result = true result = true
var i: int var i: int
for key, _ in fieldPairs(v): for key, field in fieldPairs(v):
let val = pr.getOrDefault(toSymbol(key))
result = result and fieldFromPreserve(
key, v.dot(key), val)
if not result: break if not result: break
inc i let val = step(pr, key.toSymbol)
when field is Option:
if val.isSome:
discard fieldFromPreserve(key, v.dot(key), val.get)
else:
inc i
result = result and val.isSome
if result:
result = result and fieldFromPreserve(key, v.dot(key), val.get)
result = result and (i <= pr.len) result = result and (i <= pr.len)
elif T.hasCustomPragma(preservesOr): elif T.hasCustomPragma(preservesOr):
for kind in typeof(T.orKind): for kind in typeof(T.orKind):
@ -740,9 +745,11 @@ proc fromPreserves*[T](v: var T; pr: Value): bool {.gcsafe.} =
result = true result = true
var i: int var i: int
for key, _ in fieldPairs(v): for key, _ in fieldPairs(v):
let val = pr.getOrDefault(toSymbol(key))
result = result and fieldFromPreserve(key, v.dot(key), val)
if not result: break if not result: break
let val = step(pr, key.toSymbol)
result = result and val.isSome
if result:
result = result and fieldFromPreserve(key, v.dot(key), val.get)
inc i inc i
result = result and (i <= pr.len) result = result and (i <= pr.len)
else: else:
@ -811,6 +818,12 @@ proc fromPreservesHook*[A,B](t: var (Table[A,B]|TableRef[A,B]); pr: Value): bool
break break
t[move a] = move b t[move a] = move b
proc fromPreservesHook*[T](opt: var Option[T]; pr: Value): bool =
opt = some(default T)
result = opt.get.fromPreserves(pr)
if not result:
opt = none(T)
when isMainModule: when isMainModule:
var t: Table[int, string] var t: Table[int, string]
var pr = t.toPreservesHook() var pr = t.toPreservesHook()

View File

@ -17,11 +17,13 @@ suite "conversions":
a: int a: int
b: seq[int] b: seq[int]
c {.preservesEmbedded.}: Bar c {.preservesEmbedded.}: Bar
d: Option[bool]
e: Option[bool]
let let
c = Foobar(a: 1, b: @[2], c: ("ku", )) c = Foobar(a: 1, b: @[2], c: ("ku", ), e: some(true))
b = toPreserves(c) b = toPreserves(c)
a = preservesTo(b, Foobar) a = preservesTo(b, Foobar)
check($b == """{a: 1 b: [2] c: #!["ku"]}""") check($b == """{a: 1 b: [2] c: #!["ku"] e: #t}""")
check(a.isSome) check(a.isSome)
if a.isSome: check(get(a) == c) if a.isSome: check(get(a) == c)
check(b.kind == pkDictionary) check(b.kind == pkDictionary)