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
|
||||
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
|
||||
---------------
|
||||
|
||||
|
@ -25,6 +25,10 @@ type
|
||||
strVal: string
|
||||
of ckInt:
|
||||
intVal: int
|
||||
|
||||
Sparse {.sparse.} = ref object of RootObj
|
||||
name*: Option[string]
|
||||
description*: Option[string]
|
||||
|
||||
suite "Serialization Annotations":
|
||||
test "load default value":
|
||||
@ -59,4 +63,11 @@ suite "Serialization Annotations":
|
||||
load(input, result)
|
||||
assert len(result) == 2
|
||||
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.
|
||||
|
||||
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
|
||||
## annotation is used as value.
|
||||
##
|
||||
@ -23,6 +23,19 @@ template defaultVal*(value : typed) {.pragma.}
|
||||
## a {.defaultVal: "foo".}: string
|
||||
## 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.}
|
||||
## 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
|
||||
|
@ -1,5 +1,5 @@
|
||||
# NimYAML - YAML implementation in Nim
|
||||
# (c) Copyright 2016 Felix Krause
|
||||
# (c) Copyright 2016 - 2020 Felix Krause
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
import tables, typetraits, strutils, macros, streams, times, parseutils, options
|
||||
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),
|
||||
# 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)
|
||||
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,
|
||||
i: int, matched, o: NimNode):
|
||||
NimNode {.compileTime.} =
|
||||
let fName = escape($field)
|
||||
let
|
||||
fType = getTypeInst(field)
|
||||
fName = escape($field)
|
||||
optionInner = getOptionInner(fType)
|
||||
result = quote do:
|
||||
when not `o`.`field`.hasCustomPragma(transient):
|
||||
if not `matched`[`i`]:
|
||||
when `o`.`field`.hasCustomPragma(defaultVal):
|
||||
`o`.`field` = `o`.`field`.getCustomPragmaVal(defaultVal)
|
||||
elif hasSparse(`t`) and `o`.`field` is Option:
|
||||
`o`.`field` = none(`optionInner`)
|
||||
else:
|
||||
raise constructionError(`s`, "While constructing " & `tName` &
|
||||
": Missing field: " & `fName`)
|
||||
@ -700,7 +717,8 @@ macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed,
|
||||
var field = 0
|
||||
for child in tDesc[2].children:
|
||||
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:
|
||||
let discChecks = newStmtList()
|
||||
var
|
||||
@ -716,7 +734,8 @@ macro ensureAllFieldsPresent(s: YamlStream, t: typedesc, o: typed,
|
||||
else: internalError("Unexpected child kind: " & $child[bIndex].kind)
|
||||
for item in child[bIndex][recListIndex].recListItems:
|
||||
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])),
|
||||
"in", curValues), discChecks)))
|
||||
else:
|
||||
|
Loading…
x
Reference in New Issue
Block a user