mirror of
https://github.com/status-im/NimYAML.git
synced 2025-01-12 20:44:46 +00:00
added {.sparse.}. fixes #82
This commit is contained in:
parent
e4b3e8347a
commit
951efdf18e
@ -93,6 +93,15 @@ You can simply load it into a `seq[int]`. If your YAML file contains differently
|
|||||||
typed values in the same collection, you can use an implicit variant object, see
|
typed values in the same collection, you can use an implicit variant object, see
|
||||||
below.
|
below.
|
||||||
|
|
||||||
|
A special case is ``Option[T]``: This type will either contain a value or not.
|
||||||
|
NimYAML maps ``!!null`` YAML scalars to the option's ``none(T)`` value.
|
||||||
|
This also works for ``ref`` types because ``Option`` for those types will use
|
||||||
|
``nil`` as its ``none(T)`` value.
|
||||||
|
|
||||||
|
By default, ``Option`` fields must be given even if they are ``none(T)``.
|
||||||
|
You can circumvent this by putting the annotation ``{.sparse.}`` on the type
|
||||||
|
containing the ``Option`` field.
|
||||||
|
|
||||||
Reference Types
|
Reference Types
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
@ -25,6 +25,10 @@ type
|
|||||||
strVal: string
|
strVal: string
|
||||||
of ckInt:
|
of ckInt:
|
||||||
intVal: int
|
intVal: int
|
||||||
|
|
||||||
|
Sparse {.sparse.} = ref object of RootObj
|
||||||
|
name*: Option[string]
|
||||||
|
description*: Option[string]
|
||||||
|
|
||||||
suite "Serialization Annotations":
|
suite "Serialization Annotations":
|
||||||
test "load default value":
|
test "load default value":
|
||||||
@ -59,4 +63,11 @@ suite "Serialization Annotations":
|
|||||||
load(input, result)
|
load(input, result)
|
||||||
assert len(result) == 2
|
assert len(result) == 2
|
||||||
assert result[0].kind == ckString
|
assert result[0].kind == ckString
|
||||||
assert result[1].kind == ckInt
|
assert result[1].kind == ckInt
|
||||||
|
|
||||||
|
test "load sparse type":
|
||||||
|
let input = "{}"
|
||||||
|
var result: Sparse
|
||||||
|
load(input, result)
|
||||||
|
assert result.name.isNone
|
||||||
|
assert result.description.isNone
|
@ -12,7 +12,7 @@
|
|||||||
## (de)serialization behavior of those fields.
|
## (de)serialization behavior of those fields.
|
||||||
|
|
||||||
template defaultVal*(value : typed) {.pragma.}
|
template defaultVal*(value : typed) {.pragma.}
|
||||||
## This annotation can be assigned to an object field. During deserialization,
|
## This annotation can be put on an object field. During deserialization,
|
||||||
## if no value for this field is given, the ``value`` parameter of this
|
## if no value for this field is given, the ``value`` parameter of this
|
||||||
## annotation is used as value.
|
## annotation is used as value.
|
||||||
##
|
##
|
||||||
@ -23,6 +23,19 @@ template defaultVal*(value : typed) {.pragma.}
|
|||||||
## a {.defaultVal: "foo".}: string
|
## a {.defaultVal: "foo".}: string
|
||||||
## c {.defaultVal: (1,2).}: tuple[x, y: int]
|
## c {.defaultVal: (1,2).}: tuple[x, y: int]
|
||||||
|
|
||||||
|
template sparse*() {.pragma.}
|
||||||
|
## This annotation can be put on an object type. During deserialization,
|
||||||
|
## the input may omit any field that has an ``Option[T]`` type (for any
|
||||||
|
## concrete ``T``) and that field will be treated as if it had the annotation
|
||||||
|
## ``{.defaultVal: none(T).}``.
|
||||||
|
##
|
||||||
|
## Example usage:
|
||||||
|
##
|
||||||
|
## .. code-block::
|
||||||
|
## type MyObject {.sparse.} = object
|
||||||
|
## a: Option[string]
|
||||||
|
## b: Option[int]
|
||||||
|
|
||||||
template transient*() {.pragma.}
|
template transient*() {.pragma.}
|
||||||
## This annotation can be put on an object field. Any object field
|
## This annotation can be put on an object field. Any object field
|
||||||
## carrying this annotation will not be serialized to YAML and cannot be given
|
## carrying this annotation will not be serialized to YAML and cannot be given
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# NimYAML - YAML implementation in Nim
|
# NimYAML - YAML implementation in Nim
|
||||||
# (c) Copyright 2016 Felix Krause
|
# (c) Copyright 2016 - 2020 Felix Krause
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
import tables, typetraits, strutils, macros, streams, times, parseutils, options
|
import tables, typetraits, strutils, macros, streams, times, parseutils, options
|
||||||
import parser, taglib, presenter, stream, private/internal, hints, annotations
|
import parser, taglib, presenter, stream, private/internal, hints, annotations
|
||||||
export stream, macros, annotations
|
export stream, macros, annotations, options
|
||||||
# *something* in here needs externally visible `==`(x,y: AnchorId),
|
# *something* in here needs externally visible `==`(x,y: AnchorId),
|
||||||
# but I cannot figure out what. binding it would be the better option.
|
# but I cannot figure out what. binding it would be the better option.
|
||||||
|
|
||||||
@ -656,15 +656,32 @@ proc addDefaultOr(tName: string, i: int, o: NimNode,
|
|||||||
`o`.`field` = `o`.`field`.getCustomPragmaVal(defaultVal)
|
`o`.`field` = `o`.`field`.getCustomPragmaVal(defaultVal)
|
||||||
else: `elseBranch`
|
else: `elseBranch`
|
||||||
|
|
||||||
|
proc hasSparse(t: typedesc): bool {.compileTime.} =
|
||||||
|
when compiles(t.hasCustomPragma(sparse)):
|
||||||
|
return t.hasCustomPragma(sparse)
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc getOptionInner(fType: NimNode): NimNode {.compileTime.} =
|
||||||
|
if fType.kind == nnkBracketExpr and len(fType) == 2 and
|
||||||
|
fType[1].kind == nnkSym:
|
||||||
|
return newIdentNode($fType[1])
|
||||||
|
else: return nil
|
||||||
|
|
||||||
proc checkMissing(s: NimNode, t: NimNode, tName: string, field: NimNode,
|
proc checkMissing(s: NimNode, t: NimNode, tName: string, field: NimNode,
|
||||||
i: int, matched, o: NimNode):
|
i: int, matched, o: NimNode):
|
||||||
NimNode {.compileTime.} =
|
NimNode {.compileTime.} =
|
||||||
let fName = escape($field)
|
let
|
||||||
|
fType = getTypeInst(field)
|
||||||
|
fName = escape($field)
|
||||||
|
optionInner = getOptionInner(fType)
|
||||||
result = quote do:
|
result = quote do:
|
||||||
when not `o`.`field`.hasCustomPragma(transient):
|
when not `o`.`field`.hasCustomPragma(transient):
|
||||||
if not `matched`[`i`]:
|
if not `matched`[`i`]:
|
||||||
when `o`.`field`.hasCustomPragma(defaultVal):
|
when `o`.`field`.hasCustomPragma(defaultVal):
|
||||||
`o`.`field` = `o`.`field`.getCustomPragmaVal(defaultVal)
|
`o`.`field` = `o`.`field`.getCustomPragmaVal(defaultVal)
|
||||||
|
elif hasSparse(`t`) and `o`.`field` is Option:
|
||||||
|
`o`.`field` = none(`optionInner`)
|
||||||
else:
|
else:
|
||||||
raise constructionError(`s`, "While constructing " & `tName` &
|
raise constructionError(`s`, "While constructing " & `tName` &
|
||||||
": Missing field: " & `fName`)
|
": Missing field: " & `fName`)
|
||||||
@ -700,7 +717,8 @@ macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed,
|
|||||||
var field = 0
|
var field = 0
|
||||||
for child in tDesc[2].children:
|
for child in tDesc[2].children:
|
||||||
if child.kind == nnkRecCase:
|
if child.kind == nnkRecCase:
|
||||||
result.add(checkMissing(s, t, tName, child[0], field, matched, o))
|
result.add(checkMissing(
|
||||||
|
s, t, tName, child[0], field, matched, o))
|
||||||
for bIndex in 1 .. len(child) - 1:
|
for bIndex in 1 .. len(child) - 1:
|
||||||
let discChecks = newStmtList()
|
let discChecks = newStmtList()
|
||||||
var
|
var
|
||||||
@ -716,7 +734,8 @@ macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed,
|
|||||||
else: internalError("Unexpected child kind: " & $child[bIndex].kind)
|
else: internalError("Unexpected child kind: " & $child[bIndex].kind)
|
||||||
for item in child[bIndex][recListIndex].recListItems:
|
for item in child[bIndex][recListIndex].recListItems:
|
||||||
inc(field)
|
inc(field)
|
||||||
discChecks.add(checkMissing(s, t, tName, item, field, matched, o))
|
discChecks.add(checkMissing(
|
||||||
|
s, t, tName, item, field, matched, o))
|
||||||
result.add(newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])),
|
result.add(newIfStmt((infix(newDotExpr(o, newIdentNode($child[0])),
|
||||||
"in", curValues), discChecks)))
|
"in", curValues), discChecks)))
|
||||||
else:
|
else:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user