chore: format using nph
This commit is contained in:
parent
88ce7fb56c
commit
681f32aba6
|
@ -10,4 +10,4 @@ export deserializer
|
|||
export stdjson
|
||||
export pragmas
|
||||
export serializer
|
||||
export types
|
||||
export types
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import std/macros
|
||||
import std/options
|
||||
import std/sequtils
|
||||
|
@ -32,22 +31,21 @@ logScope:
|
|||
topics = "json deserialization"
|
||||
|
||||
template expectJsonKind(
|
||||
expectedType: type,
|
||||
expectedKinds: set[JsonNodeKind],
|
||||
json: JsonNode
|
||||
expectedType: type, expectedKinds: set[JsonNodeKind], json: JsonNode
|
||||
) =
|
||||
if json.isNil or json.kind notin expectedKinds:
|
||||
return failure(newUnexpectedKindError(expectedType, expectedKinds, json))
|
||||
|
||||
template expectJsonKind*(
|
||||
expectedType: type,
|
||||
expectedKind: JsonNodeKind,
|
||||
json: JsonNode
|
||||
) =
|
||||
template expectJsonKind*(expectedType: type, expectedKind: JsonNodeKind, json: JsonNode) =
|
||||
expectJsonKind(expectedType, {expectedKind}, json)
|
||||
|
||||
proc fieldKeys[T](obj: T): seq[string] =
|
||||
for name, _ in fieldPairs(when type(T) is ref: obj[] else: obj):
|
||||
for name, _ in fieldPairs(
|
||||
when type(T) is ref:
|
||||
obj[]
|
||||
else:
|
||||
obj
|
||||
):
|
||||
result.add name
|
||||
|
||||
func keysNotIn[T](json: JsonNode, obj: T): HashSet[string] =
|
||||
|
@ -55,20 +53,13 @@ func keysNotIn[T](json: JsonNode, obj: T): HashSet[string] =
|
|||
let objKeys = obj.fieldKeys.toHashSet
|
||||
difference(jsonKeys, objKeys)
|
||||
|
||||
|
||||
proc fromJson*(
|
||||
T: type enum,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
proc fromJson*(T: type enum, json: JsonNode): ?!T =
|
||||
expectJsonKind(string, JString, json)
|
||||
without val =? parseEnum[T](json.str).catch, error:
|
||||
return failure error.mapErrTo(SerdeError)
|
||||
return success val
|
||||
|
||||
proc fromJson*(
|
||||
_: type string,
|
||||
json: JsonNode
|
||||
): ?!string =
|
||||
proc fromJson*(_: type string, json: JsonNode): ?!string =
|
||||
if json.isNil:
|
||||
return failure newSerdeError("'json' expected, but was nil")
|
||||
elif json.kind == JNull:
|
||||
|
@ -77,25 +68,16 @@ proc fromJson*(
|
|||
return failure newUnexpectedKindError(string, JString, json)
|
||||
catch json.getStr
|
||||
|
||||
proc fromJson*(
|
||||
_: type bool,
|
||||
json: JsonNode
|
||||
): ?!bool =
|
||||
proc fromJson*(_: type bool, json: JsonNode): ?!bool =
|
||||
expectJsonKind(bool, JBool, json)
|
||||
catch json.getBool
|
||||
|
||||
proc fromJson*(
|
||||
_: type int,
|
||||
json: JsonNode
|
||||
): ?!int =
|
||||
proc fromJson*(_: type int, json: JsonNode): ?!int =
|
||||
expectJsonKind(int, JInt, json)
|
||||
catch json.getInt
|
||||
|
||||
proc fromJson*[T: SomeInteger](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
when T is uint|uint64 or (not defined(js) and int.sizeof == 4):
|
||||
proc fromJson*[T: SomeInteger](_: type T, json: JsonNode): ?!T =
|
||||
when T is uint | uint64 or (not defined(js) and int.sizeof == 4):
|
||||
expectJsonKind(T, {JInt, JString}, json)
|
||||
case json.kind
|
||||
of JString:
|
||||
|
@ -108,18 +90,16 @@ proc fromJson*[T: SomeInteger](
|
|||
expectJsonKind(T, {JInt}, json)
|
||||
return success cast[T](json.num)
|
||||
|
||||
proc fromJson*[T: SomeFloat](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
proc fromJson*[T: SomeFloat](_: type T, json: JsonNode): ?!T =
|
||||
expectJsonKind(T, {JInt, JFloat, JString}, json)
|
||||
if json.kind == JString:
|
||||
case json.str
|
||||
of "nan":
|
||||
let b = NaN
|
||||
return success T(b)
|
||||
# dst = NaN # would fail some tests because range conversions would cause CT error
|
||||
# in some cases; but this is not a hot-spot inside this branch and backend can optimize this.
|
||||
# dst = NaN would fail some tests because range conversions would cause
|
||||
# CT error in some cases; but this is not a hot-spot inside this branch
|
||||
# and backend can optimize this.
|
||||
of "inf":
|
||||
let b = Inf
|
||||
return success T(b)
|
||||
|
@ -135,69 +115,55 @@ proc fromJson*[T: SomeFloat](
|
|||
else:
|
||||
return success T(json.num)
|
||||
|
||||
proc fromJson*(
|
||||
_: type seq[byte],
|
||||
json: JsonNode
|
||||
): ?!seq[byte] =
|
||||
proc fromJson*(_: type seq[byte], json: JsonNode): ?!seq[byte] =
|
||||
expectJsonKind(seq[byte], JString, json)
|
||||
hexToSeqByte(json.getStr).catch
|
||||
|
||||
proc fromJson*[N: static[int], T: array[N, byte]](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
proc fromJson*[N: static[int], T: array[N, byte]](_: type T, json: JsonNode): ?!T =
|
||||
expectJsonKind(T, JString, json)
|
||||
T.fromHex(json.getStr).catch
|
||||
|
||||
proc fromJson*[T: distinct](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
success T(? T.distinctBase.fromJson(json))
|
||||
proc fromJson*[T: distinct](_: type T, json: JsonNode): ?!T =
|
||||
success T(?T.distinctBase.fromJson(json))
|
||||
|
||||
proc fromJson*[N: static[int], T: StUint[N]](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
proc fromJson*[N: static[int], T: StUint[N]](_: type T, json: JsonNode): ?!T =
|
||||
expectJsonKind(T, JString, json)
|
||||
let jsonStr = json.getStr
|
||||
let prefix = jsonStr[0..1].toLowerAscii
|
||||
case prefix:
|
||||
of "0x": catch parse(jsonStr, T, 16)
|
||||
of "0o": catch parse(jsonStr, T, 8)
|
||||
of "0b": catch parse(jsonStr, T, 2)
|
||||
else: catch parse(jsonStr, T)
|
||||
let prefix = jsonStr[0 .. 1].toLowerAscii
|
||||
case prefix
|
||||
of "0x":
|
||||
catch parse(jsonStr, T, 16)
|
||||
of "0o":
|
||||
catch parse(jsonStr, T, 8)
|
||||
of "0b":
|
||||
catch parse(jsonStr, T, 2)
|
||||
else:
|
||||
catch parse(jsonStr, T)
|
||||
|
||||
proc fromJson*[T](
|
||||
_: type Option[T],
|
||||
json: JsonNode
|
||||
): ?! Option[T] =
|
||||
proc fromJson*[T](_: type Option[T], json: JsonNode): ?!Option[T] =
|
||||
if json.isNil or json.kind == JNull:
|
||||
return success(none T)
|
||||
without val =? T.fromJson(json), error:
|
||||
return failure(error)
|
||||
success(val.some)
|
||||
|
||||
proc fromJson*[T](
|
||||
_: type seq[T],
|
||||
json: JsonNode
|
||||
): ?! seq[T] =
|
||||
proc fromJson*[T](_: type seq[T], json: JsonNode): ?!seq[T] =
|
||||
expectJsonKind(seq[T], JArray, json)
|
||||
var arr: seq[T] = @[]
|
||||
for elem in json.elems:
|
||||
arr.add(? T.fromJson(elem))
|
||||
arr.add(?T.fromJson(elem))
|
||||
success arr
|
||||
|
||||
proc fromJson*[T: ref object or object](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
|
||||
proc fromJson*[T: ref object or object](_: type T, json: JsonNode): ?!T =
|
||||
when T is JsonNode:
|
||||
return success T(json)
|
||||
|
||||
expectJsonKind(T, JObject, json)
|
||||
var res = when type(T) is ref: T.new() else: T.default
|
||||
var res =
|
||||
when type(T) is ref:
|
||||
T.new()
|
||||
else:
|
||||
T.default
|
||||
let mode = T.getSerdeMode(deserialize)
|
||||
|
||||
# ensure there's no extra fields in json
|
||||
|
@ -206,8 +172,12 @@ proc fromJson*[T: ref object or object](
|
|||
if extraFields.len > 0:
|
||||
return failure newSerdeError("json field(s) missing in object: " & $extraFields)
|
||||
|
||||
for name, value in fieldPairs(when type(T) is ref: res[] else: res):
|
||||
|
||||
for name, value in fieldPairs(
|
||||
when type(T) is ref:
|
||||
res[]
|
||||
else:
|
||||
res
|
||||
):
|
||||
logScope:
|
||||
field = $T & "." & name
|
||||
mode
|
||||
|
@ -217,14 +187,13 @@ proc fromJson*[T: ref object or object](
|
|||
let isOptionalValue = typeof(value) is Option
|
||||
var skip = false # workaround for 'continue' not supported in a 'fields' loop
|
||||
|
||||
case mode:
|
||||
case mode
|
||||
of Strict:
|
||||
if opts.key notin json:
|
||||
return failure newSerdeError("object field missing in json: " & opts.key)
|
||||
elif opts.ignore:
|
||||
# unable to figure out a way to make this a compile time check
|
||||
warn "object field marked as 'ignore' while in Strict mode, field will be deserialized anyway"
|
||||
|
||||
of OptIn:
|
||||
if not hasDeserializePragma:
|
||||
debug "object field not marked as 'deserialize', skipping"
|
||||
|
@ -234,7 +203,6 @@ proc fromJson*[T: ref object or object](
|
|||
skip = true
|
||||
elif opts.key notin json and not isOptionalValue:
|
||||
return failure newSerdeError("object field missing in json: " & opts.key)
|
||||
|
||||
of OptOut:
|
||||
if opts.ignore:
|
||||
debug "object field is opted out of deserialization ('ignore' is set), skipping"
|
||||
|
@ -243,49 +211,31 @@ proc fromJson*[T: ref object or object](
|
|||
warn "object field marked as deserialize in OptOut mode, but 'ignore' not set, field will be deserialized"
|
||||
|
||||
if not skip:
|
||||
|
||||
if isOptionalValue:
|
||||
|
||||
let jsonVal = json{opts.key}
|
||||
without parsed =? typeof(value).fromJson(jsonVal), e:
|
||||
debug "failed to deserialize field",
|
||||
`type` = $typeof(value),
|
||||
json = jsonVal,
|
||||
error = e.msg
|
||||
`type` = $typeof(value), json = jsonVal, error = e.msg
|
||||
return failure(e)
|
||||
value = parsed
|
||||
|
||||
# not Option[T]
|
||||
elif opts.key in json and
|
||||
jsonVal =? json{opts.key}.catch and
|
||||
not jsonVal.isNil:
|
||||
|
||||
elif opts.key in json and jsonVal =? json{opts.key}.catch and not jsonVal.isNil:
|
||||
without parsed =? typeof(value).fromJson(jsonVal), e:
|
||||
debug "failed to deserialize field",
|
||||
`type` = $typeof(value),
|
||||
json = jsonVal,
|
||||
error = e.msg
|
||||
`type` = $typeof(value), json = jsonVal, error = e.msg
|
||||
return failure(e)
|
||||
value = parsed
|
||||
|
||||
success(res)
|
||||
|
||||
proc fromJson*[T: ref object or object](
|
||||
_: type T,
|
||||
bytes: seq[byte]
|
||||
): ?!T =
|
||||
let json = ? parse(string.fromBytes(bytes))
|
||||
proc fromJson*[T: ref object or object](_: type T, bytes: seq[byte]): ?!T =
|
||||
let json = ?parse(string.fromBytes(bytes))
|
||||
T.fromJson(json)
|
||||
|
||||
proc fromJson*(
|
||||
_: type JsonNode,
|
||||
jsn: string
|
||||
): ?!JsonNode =
|
||||
proc fromJson*(_: type JsonNode, jsn: string): ?!JsonNode =
|
||||
return parser.parseJson(jsn)
|
||||
|
||||
proc fromJson*[T: ref object or object](
|
||||
_: type T,
|
||||
jsn: string
|
||||
): ?!T =
|
||||
let jsn = ? parser.parseJson(jsn) # full qualification required in-module only
|
||||
proc fromJson*[T: ref object or object](_: type T, jsn: string): ?!T =
|
||||
let jsn = ?parser.parseJson(jsn) # full qualification required in-module only
|
||||
T.fromJson(jsn)
|
||||
|
|
|
@ -6,36 +6,33 @@ import ./types
|
|||
{.push raises: [].}
|
||||
|
||||
proc mapErrTo*[E1: ref CatchableError, E2: SerdeError](
|
||||
e1: E1,
|
||||
_: type E2,
|
||||
msg: string = e1.msg): ref E2 =
|
||||
|
||||
e1: E1, _: type E2, msg: string = e1.msg
|
||||
): ref E2 =
|
||||
return newException(E2, msg, e1)
|
||||
|
||||
proc newSerdeError*(msg: string): ref SerdeError =
|
||||
newException(SerdeError, msg)
|
||||
|
||||
proc newUnexpectedKindError*(
|
||||
expectedType: type,
|
||||
expectedKinds: string,
|
||||
json: JsonNode): ref UnexpectedKindError =
|
||||
|
||||
let kind = if json.isNil: "nil"
|
||||
else: $json.kind
|
||||
newException(UnexpectedKindError,
|
||||
"deserialization to " & $expectedType & " failed: expected " &
|
||||
expectedKinds & " but got " & $kind)
|
||||
expectedType: type, expectedKinds: string, json: JsonNode
|
||||
): ref UnexpectedKindError =
|
||||
let kind =
|
||||
if json.isNil:
|
||||
"nil"
|
||||
else:
|
||||
$json.kind
|
||||
newException(
|
||||
UnexpectedKindError,
|
||||
"deserialization to " & $expectedType & " failed: expected " & expectedKinds &
|
||||
" but got " & $kind,
|
||||
)
|
||||
|
||||
proc newUnexpectedKindError*(
|
||||
expectedType: type,
|
||||
expectedKinds: set[JsonNodeKind],
|
||||
json: JsonNode): ref UnexpectedKindError =
|
||||
|
||||
expectedType: type, expectedKinds: set[JsonNodeKind], json: JsonNode
|
||||
): ref UnexpectedKindError =
|
||||
newUnexpectedKindError(expectedType, $expectedKinds, json)
|
||||
|
||||
proc newUnexpectedKindError*(
|
||||
expectedType: type,
|
||||
expectedKind: JsonNodeKind,
|
||||
json: JsonNode): ref UnexpectedKindError =
|
||||
|
||||
expectedType: type, expectedKind: JsonNodeKind, json: JsonNode
|
||||
): ref UnexpectedKindError =
|
||||
newUnexpectedKindError(expectedType, {expectedKind}, json)
|
||||
|
|
|
@ -12,22 +12,26 @@ template deserialize*(key = "", ignore = false, mode = SerdeMode.OptOut) {.pragm
|
|||
proc isDefault[T](paramValue: T): bool {.compileTime.} =
|
||||
when T is SerdeMode:
|
||||
return paramValue == SerdeMode.OptOut
|
||||
else: return paramValue == T.default
|
||||
else:
|
||||
return paramValue == T.default
|
||||
|
||||
template expectMissingPragmaParam*(value, pragma, name, msg) =
|
||||
static:
|
||||
when value.hasCustomPragma(pragma):
|
||||
const params = value.getCustomPragmaVal(pragma)
|
||||
for paramName, paramValue in params.fieldPairs:
|
||||
|
||||
if paramName == name and not paramValue.isDefault:
|
||||
raiseAssert(msg)
|
||||
|
||||
template getSerdeFieldOptions*(pragma, fieldName, fieldValue): SerdeFieldOptions =
|
||||
var opts = SerdeFieldOptions(key: fieldName, ignore: false)
|
||||
when fieldValue.hasCustomPragma(pragma):
|
||||
fieldValue.expectMissingPragmaParam(pragma, "mode",
|
||||
"Cannot set " & astToStr(pragma) & " 'mode' on '" & fieldName & "' field defintion.")
|
||||
fieldValue.expectMissingPragmaParam(
|
||||
pragma,
|
||||
"mode",
|
||||
"Cannot set " & astToStr(pragma) & " 'mode' on '" & fieldName &
|
||||
"' field defintion.",
|
||||
)
|
||||
let (key, ignore, _) = fieldValue.getCustomPragmaVal(pragma)
|
||||
opts.ignore = ignore
|
||||
if key != "":
|
||||
|
@ -36,12 +40,16 @@ template getSerdeFieldOptions*(pragma, fieldName, fieldValue): SerdeFieldOptions
|
|||
|
||||
template getSerdeMode*(T, pragma): SerdeMode =
|
||||
when T.hasCustomPragma(pragma):
|
||||
T.expectMissingPragmaParam(pragma, "key",
|
||||
"Cannot set " & astToStr(pragma) & " 'key' on '" & $T &
|
||||
"' type definition.")
|
||||
T.expectMissingPragmaParam(pragma, "ignore",
|
||||
"Cannot set " & astToStr(pragma) & " 'ignore' on '" & $T &
|
||||
"' type definition.")
|
||||
T.expectMissingPragmaParam(
|
||||
pragma,
|
||||
"key",
|
||||
"Cannot set " & astToStr(pragma) & " 'key' on '" & $T & "' type definition.",
|
||||
)
|
||||
T.expectMissingPragmaParam(
|
||||
pragma,
|
||||
"ignore",
|
||||
"Cannot set " & astToStr(pragma) & " 'ignore' on '" & $T & "' type definition.",
|
||||
)
|
||||
let (_, _, mode) = T.getCustomPragmaVal(pragma)
|
||||
mode
|
||||
else:
|
||||
|
|
|
@ -23,7 +23,8 @@ export types
|
|||
logScope:
|
||||
topics = "json serialization"
|
||||
|
||||
func `%`*(s: string): JsonNode = newJString(s)
|
||||
func `%`*(s: string): JsonNode =
|
||||
newJString(s)
|
||||
|
||||
func `%`*(n: uint): JsonNode =
|
||||
if n > cast[uint](int.high):
|
||||
|
@ -31,7 +32,8 @@ func `%`*(n: uint): JsonNode =
|
|||
else:
|
||||
newJInt(BiggestInt(n))
|
||||
|
||||
func `%`*(n: int): JsonNode = newJInt(n)
|
||||
func `%`*(n: int): JsonNode =
|
||||
newJInt(n)
|
||||
|
||||
func `%`*(n: BiggestUInt): JsonNode =
|
||||
if n > cast[BiggestUInt](BiggestInt.high):
|
||||
|
@ -39,42 +41,56 @@ func `%`*(n: BiggestUInt): JsonNode =
|
|||
else:
|
||||
newJInt(BiggestInt(n))
|
||||
|
||||
func `%`*(n: BiggestInt): JsonNode = newJInt(n)
|
||||
func `%`*(n: BiggestInt): JsonNode =
|
||||
newJInt(n)
|
||||
|
||||
func `%`*(n: float): JsonNode =
|
||||
if n != n: newJString("nan")
|
||||
elif n == Inf: newJString("inf")
|
||||
elif n == -Inf: newJString("-inf")
|
||||
else: newJFloat(n)
|
||||
if n != n:
|
||||
newJString("nan")
|
||||
elif n == Inf:
|
||||
newJString("inf")
|
||||
elif n == -Inf:
|
||||
newJString("-inf")
|
||||
else:
|
||||
newJFloat(n)
|
||||
|
||||
func `%`*(b: bool): JsonNode = newJBool(b)
|
||||
func `%`*(b: bool): JsonNode =
|
||||
newJBool(b)
|
||||
|
||||
func `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode =
|
||||
if keyVals.len == 0: return newJArray()
|
||||
if keyVals.len == 0:
|
||||
return newJArray()
|
||||
let jObj = newJObject()
|
||||
for key, val in items(keyVals): jObj.fields[key] = val
|
||||
for key, val in items(keyVals):
|
||||
jObj.fields[key] = val
|
||||
jObj
|
||||
|
||||
template `%`*(j: JsonNode): JsonNode = j
|
||||
template `%`*(j: JsonNode): JsonNode =
|
||||
j
|
||||
|
||||
func `%`*[T](table: Table[string, T]|OrderedTable[string, T]): JsonNode =
|
||||
func `%`*[T](table: Table[string, T] | OrderedTable[string, T]): JsonNode =
|
||||
let jObj = newJObject()
|
||||
for k, v in table: jObj[k] = ? %v
|
||||
for k, v in table:
|
||||
jObj[k] = ? %v
|
||||
jObj
|
||||
|
||||
func `%`*[T](opt: Option[T]): JsonNode =
|
||||
if opt.isSome: %(opt.get) else: newJNull()
|
||||
if opt.isSome:
|
||||
%(opt.get)
|
||||
else:
|
||||
newJNull()
|
||||
|
||||
proc `%`*[T: object or ref object](obj: T): JsonNode =
|
||||
|
||||
let jsonObj = newJObject()
|
||||
let o = when T is ref object: obj[]
|
||||
else: obj
|
||||
let o =
|
||||
when T is ref object:
|
||||
obj[]
|
||||
else:
|
||||
obj
|
||||
|
||||
let mode = T.getSerdeMode(serialize)
|
||||
|
||||
for name, value in o.fieldPairs:
|
||||
|
||||
logScope:
|
||||
field = $T & "." & name
|
||||
mode
|
||||
|
@ -83,21 +99,19 @@ proc `%`*[T: object or ref object](obj: T): JsonNode =
|
|||
let hasSerialize = value.hasCustomPragma(serialize)
|
||||
var skip = false # workaround for 'continue' not supported in a 'fields' loop
|
||||
|
||||
case mode:
|
||||
case mode
|
||||
of OptIn:
|
||||
if not hasSerialize:
|
||||
debug "object field not marked with serialize, skipping"
|
||||
skip = true
|
||||
elif opts.ignore:
|
||||
skip = true
|
||||
|
||||
of OptOut:
|
||||
if opts.ignore:
|
||||
debug "object field opted out of serialization ('ignore' is set), skipping"
|
||||
skip = true
|
||||
elif hasSerialize and opts.key == name: # all serialize params are default
|
||||
warn "object field marked as serialize in OptOut mode, but 'ignore' not set, field will be serialized"
|
||||
|
||||
of Strict:
|
||||
if opts.ignore:
|
||||
# unable to figure out a way to make this a compile time check
|
||||
|
@ -108,22 +122,27 @@ proc `%`*[T: object or ref object](obj: T): JsonNode =
|
|||
|
||||
jsonObj
|
||||
|
||||
proc `%`*(o: enum): JsonNode = % $o
|
||||
proc `%`*(o: enum): JsonNode =
|
||||
% $o
|
||||
|
||||
func `%`*(stint: StInt|StUint): JsonNode = %stint.toString
|
||||
func `%`*(stint: StInt | StUint): JsonNode =
|
||||
%stint.toString
|
||||
|
||||
func `%`*(cstr: cstring): JsonNode = % $cstr
|
||||
func `%`*(cstr: cstring): JsonNode =
|
||||
% $cstr
|
||||
|
||||
func `%`*(arr: openArray[byte]): JsonNode = % arr.to0xHex
|
||||
func `%`*(arr: openArray[byte]): JsonNode =
|
||||
%arr.to0xHex
|
||||
|
||||
func `%`*[T](elements: openArray[T]): JsonNode =
|
||||
let jObj = newJArray()
|
||||
for elem in elements: jObj.add(%elem)
|
||||
for elem in elements:
|
||||
jObj.add(%elem)
|
||||
jObj
|
||||
|
||||
func `%`*[T: distinct](id: T): JsonNode =
|
||||
type baseType = T.distinctBase
|
||||
% baseType(id)
|
||||
%baseType(id)
|
||||
|
||||
proc toJson*[T](item: T, pretty = false): string =
|
||||
if pretty:
|
||||
|
@ -134,13 +153,15 @@ proc toJson*[T](item: T, pretty = false): string =
|
|||
proc toJsnImpl(x: NimNode): NimNode =
|
||||
case x.kind
|
||||
of nnkBracket: # array
|
||||
if x.len == 0: return newCall(bindSym"newJArray")
|
||||
if x.len == 0:
|
||||
return newCall(bindSym"newJArray")
|
||||
result = newNimNode(nnkBracket)
|
||||
for i in 0 ..< x.len:
|
||||
result.add(toJsnImpl(x[i]))
|
||||
result = newCall(bindSym("%", brOpen), result)
|
||||
of nnkTableConstr: # object
|
||||
if x.len == 0: return newCall(bindSym"newJObject")
|
||||
if x.len == 0:
|
||||
return newCall(bindSym"newJObject")
|
||||
result = newNimNode(nnkTableConstr)
|
||||
for i in 0 ..< x.len:
|
||||
x[i].expectKind nnkExprColonExpr
|
||||
|
@ -152,12 +173,14 @@ proc toJsnImpl(x: NimNode): NimNode =
|
|||
of nnkNilLit:
|
||||
result = newCall(bindSym"newJNull")
|
||||
of nnkPar:
|
||||
if x.len == 1: result = toJsnImpl(x[0])
|
||||
else: result = newCall(bindSym("%", brOpen), x)
|
||||
if x.len == 1:
|
||||
result = toJsnImpl(x[0])
|
||||
else:
|
||||
result = newCall(bindSym("%", brOpen), x)
|
||||
else:
|
||||
result = newCall(bindSym("%", brOpen), x)
|
||||
|
||||
macro `%*`*(x: untyped): JsonNode =
|
||||
## Convert an expression to a JsonNode directly, without having to specify
|
||||
## `%` for every element.
|
||||
result = toJsnImpl(x)
|
||||
result = toJsnImpl(x)
|
||||
|
|
|
@ -3,12 +3,16 @@ type
|
|||
JsonParseError* = object of SerdeError
|
||||
UnexpectedKindError* = object of SerdeError
|
||||
SerdeMode* = enum
|
||||
OptOut, ## serialize: all object fields will be serialized, except fields marked with 'ignore'
|
||||
## deserialize: all json keys will be deserialized, no error if extra json field
|
||||
OptIn, ## serialize: only object fields marked with serialize will be serialzied
|
||||
## deserialize: only fields marked with deserialize will be deserialized
|
||||
Strict ## serialize: all object fields will be serialized, regardless if the field is marked with 'ignore'
|
||||
## deserialize: object fields and json fields must match exactly
|
||||
OptOut
|
||||
## serialize: all object fields will be serialized, except fields marked with 'ignore'
|
||||
## deserialize: all json keys will be deserialized, no error if extra json field
|
||||
OptIn
|
||||
## serialize: only object fields marked with serialize will be serialzied
|
||||
## deserialize: only fields marked with deserialize will be deserialized
|
||||
Strict
|
||||
## serialize: all object fields will be serialized, regardless if the field is marked with 'ignore'
|
||||
## deserialize: object fields and json fields must match exactly
|
||||
|
||||
SerdeFieldOptions* = object
|
||||
key*: string
|
||||
ignore*: bool
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import std/math
|
||||
import std/options
|
||||
import std/strutils
|
||||
import std/unittest
|
||||
import pkg/stint
|
||||
import pkg/serde
|
||||
|
@ -8,14 +7,14 @@ import pkg/questionable
|
|||
import pkg/questionable/results
|
||||
|
||||
suite "json serialization - deserialize":
|
||||
|
||||
test "deserializes NaN float":
|
||||
check %NaN == newJString("nan")
|
||||
|
||||
test "deserialize enum":
|
||||
type MyEnum = enum
|
||||
First,
|
||||
First
|
||||
Second
|
||||
|
||||
let json = newJString("Second")
|
||||
check !MyEnum.fromJson(json) == Second
|
||||
|
||||
|
@ -70,11 +69,13 @@ suite "json serialization - deserialize":
|
|||
|
||||
let expected = MyObj(mystring: "abc", myint: 123, myoption: some true)
|
||||
|
||||
let json = !parseJson("""{
|
||||
"mystring": "abc",
|
||||
"myint": 123,
|
||||
"myoption": true
|
||||
}""")
|
||||
let json =
|
||||
!"""{
|
||||
"mystring": "abc",
|
||||
"myint": 123,
|
||||
"myoption": true
|
||||
}""".parseJson
|
||||
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
test "ignores serialize pragma when deserializing":
|
||||
|
@ -84,10 +85,11 @@ suite "json serialization - deserialize":
|
|||
|
||||
let expected = MyObj(mystring: "abc", mybool: true)
|
||||
|
||||
let json = !parseJson("""{
|
||||
"mystring": "abc",
|
||||
"mybool": true
|
||||
}""")
|
||||
let json =
|
||||
!"""{
|
||||
"mystring": "abc",
|
||||
"mybool": true
|
||||
}""".parseJson
|
||||
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
|
@ -98,11 +100,12 @@ suite "json serialization - deserialize":
|
|||
|
||||
let expected = MyObj(mystring: "abc", mybool: true)
|
||||
|
||||
let json = !"""{
|
||||
"mystring": "abc",
|
||||
"mybool": true,
|
||||
"extra": "extra"
|
||||
}""".parseJson
|
||||
let json =
|
||||
!"""{
|
||||
"mystring": "abc",
|
||||
"mybool": true,
|
||||
"extra": "extra"
|
||||
}""".parseJson
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
test "deserializes objects with less fields":
|
||||
|
@ -112,9 +115,10 @@ suite "json serialization - deserialize":
|
|||
|
||||
let expected = MyObj(mystring: "abc", mybool: false)
|
||||
|
||||
let json = !"""{
|
||||
"mystring": "abc"
|
||||
}""".parseJson
|
||||
let json =
|
||||
!"""{
|
||||
"mystring": "abc"
|
||||
}""".parseJson
|
||||
check !MyObj.fromJson(json) == expected
|
||||
|
||||
test "deserializes ref objects":
|
||||
|
@ -124,11 +128,12 @@ suite "json serialization - deserialize":
|
|||
|
||||
let expected = MyRef(mystring: "abc", myint: 1)
|
||||
|
||||
let json = !"""{
|
||||
"mystring": "abc",
|
||||
"myint": 1
|
||||
}""".parseJson
|
||||
let json =
|
||||
!"""{
|
||||
"mystring": "abc",
|
||||
"myint": 1
|
||||
}""".parseJson
|
||||
|
||||
let deserialized = !MyRef.fromJson(json)
|
||||
check deserialized.mystring == expected.mystring
|
||||
check deserialized.myint == expected.myint
|
||||
check deserialized.myint == expected.myint
|
||||
|
|
|
@ -7,9 +7,8 @@ import pkg/questionable
|
|||
import pkg/questionable/results
|
||||
|
||||
suite "json deserialization, mode = OptIn":
|
||||
|
||||
test "deserializes only fields marked as deserialize when mode is OptIn":
|
||||
type MyObj {.deserialize(mode=OptIn).} = object
|
||||
type MyObj {.deserialize(mode = OptIn).} = object
|
||||
field1: int
|
||||
field2 {.deserialize.}: bool
|
||||
|
||||
|
@ -17,16 +16,14 @@ suite "json deserialization, mode = OptIn":
|
|||
check val == MyObj(field1: 0, field2: true)
|
||||
|
||||
test "deserializes Optional fields when mode is OptIn":
|
||||
type MyObj {.deserialize(mode=OptIn).} = object
|
||||
type MyObj {.deserialize(mode = OptIn).} = object
|
||||
field1 {.deserialize.}: bool
|
||||
field2 {.deserialize.}: Option[bool]
|
||||
|
||||
let val = !MyObj.fromJson("""{"field1":true}""")
|
||||
check val == MyObj(field1: true, field2: none bool)
|
||||
|
||||
|
||||
suite "json deserialization, mode = OptOut":
|
||||
|
||||
test "deserializes object in OptOut mode when not marked with deserialize":
|
||||
type MyObj = object
|
||||
field1: bool
|
||||
|
@ -55,7 +52,7 @@ suite "json deserialization, mode = OptOut":
|
|||
|
||||
test "does not deserialize ignored fields in OptOut mode":
|
||||
type MyObj = object
|
||||
field1 {.deserialize(ignore=true).}: bool
|
||||
field1 {.deserialize(ignore = true).}: bool
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"field1":true,"field2":true}""")
|
||||
|
@ -77,11 +74,9 @@ suite "json deserialization, mode = OptOut":
|
|||
let val = !MyObj.fromJson("""{"field2":true}""")
|
||||
check val == MyObj(field1: none bool, field2: true)
|
||||
|
||||
|
||||
suite "json deserialization, mode = Strict":
|
||||
|
||||
test "deserializes matching object and json fields when mode is Strict":
|
||||
type MyObj {.deserialize(mode=Strict).} = object
|
||||
type MyObj {.deserialize(mode = Strict).} = object
|
||||
field1: bool
|
||||
field2: bool
|
||||
|
||||
|
@ -89,7 +84,7 @@ suite "json deserialization, mode = Strict":
|
|||
check val == MyObj(field1: true, field2: true)
|
||||
|
||||
test "fails to deserialize with missing json field when mode is Strict":
|
||||
type MyObj {.deserialize(mode=Strict).} = object
|
||||
type MyObj {.deserialize(mode = Strict).} = object
|
||||
field1: bool
|
||||
field2: bool
|
||||
|
||||
|
@ -99,7 +94,7 @@ suite "json deserialization, mode = Strict":
|
|||
check r.error.msg == "object field missing in json: field1"
|
||||
|
||||
test "fails to deserialize with missing object field when mode is Strict":
|
||||
type MyObj {.deserialize(mode=Strict).} = object
|
||||
type MyObj {.deserialize(mode = Strict).} = object
|
||||
field2: bool
|
||||
|
||||
let r = MyObj.fromJson("""{"field1":true,"field2":true}""")
|
||||
|
@ -108,8 +103,8 @@ suite "json deserialization, mode = Strict":
|
|||
check r.error.msg == "json field(s) missing in object: {\"field1\"}"
|
||||
|
||||
test "deserializes ignored fields in Strict mode":
|
||||
type MyObj {.deserialize(mode=Strict).} = object
|
||||
field1 {.deserialize(ignore=true).}: bool
|
||||
type MyObj {.deserialize(mode = Strict).} = object
|
||||
field1 {.deserialize(ignore = true).}: bool
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"field1":true,"field2":true}""")
|
||||
|
|
|
@ -3,57 +3,48 @@ import std/unittest
|
|||
import pkg/serde
|
||||
|
||||
suite "json serialization pragmas":
|
||||
|
||||
test "fails to compile when object marked with 'serialize' specifies options":
|
||||
type
|
||||
MyObj {.serialize(key="test", ignore=true).} = object
|
||||
type MyObj {.serialize(key = "test", ignore = true).} = object
|
||||
|
||||
check not compiles(%MyObj())
|
||||
|
||||
test "compiles when object marked with 'serialize' only":
|
||||
type
|
||||
MyObj {.serialize.} = object
|
||||
type MyObj {.serialize.} = object
|
||||
|
||||
check compiles(%MyObj())
|
||||
|
||||
test "fails to compile when field marked with 'deserialize' specifies mode":
|
||||
type
|
||||
MyObj = object
|
||||
field {.deserialize(mode=OptIn).}: bool
|
||||
type MyObj = object
|
||||
field {.deserialize(mode = OptIn).}: bool
|
||||
|
||||
check not compiles(MyObj.fromJson("""{"field":true}"""))
|
||||
|
||||
test "compiles when object marked with 'deserialize' specifies mode":
|
||||
type
|
||||
MyObj {.deserialize(mode=OptIn).} = object
|
||||
field: bool
|
||||
type MyObj {.deserialize(mode = OptIn).} = object
|
||||
field: bool
|
||||
|
||||
check compiles(MyObj.fromJson("""{"field":true}"""))
|
||||
|
||||
test "fails to compile when object marked with 'deserialize' specifies key":
|
||||
type
|
||||
MyObj {.deserialize("test").} = object
|
||||
field: bool
|
||||
type MyObj {.deserialize("test").} = object
|
||||
field: bool
|
||||
|
||||
check not compiles(MyObj.fromJson("""{"field":true}"""))
|
||||
|
||||
test "compiles when field marked with 'deserialize' specifies key":
|
||||
type
|
||||
MyObj = object
|
||||
field {.deserialize("test").}: bool
|
||||
type MyObj = object
|
||||
field {.deserialize("test").}: bool
|
||||
|
||||
check compiles(MyObj.fromJson("""{"field":true}"""))
|
||||
|
||||
test "compiles when field marked with empty 'deserialize'":
|
||||
type
|
||||
MyObj = object
|
||||
field {.deserialize.}: bool
|
||||
type MyObj = object
|
||||
field {.deserialize.}: bool
|
||||
|
||||
check compiles(MyObj.fromJson("""{"field":true}"""))
|
||||
|
||||
test "compiles when field marked with 'serialize'":
|
||||
type
|
||||
MyObj = object
|
||||
field {.serialize.}: bool
|
||||
type MyObj = object
|
||||
field {.serialize.}: bool
|
||||
|
||||
check compiles(%MyObj())
|
||||
check compiles(%MyObj())
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import std/options
|
||||
import std/strutils
|
||||
import std/unittest
|
||||
import pkg/stint
|
||||
import pkg/serde
|
||||
|
@ -8,9 +7,8 @@ import pkg/questionable
|
|||
import ../helpers
|
||||
|
||||
suite "json serialization - serialize":
|
||||
|
||||
test "serializes UInt256 to non-hex string representation":
|
||||
check (% 100000.u256) == newJString("100000")
|
||||
check (%100000.u256) == newJString("100000")
|
||||
|
||||
test "serializes sequence to an array":
|
||||
let json = % @[1, 2, 3]
|
||||
|
@ -50,10 +48,7 @@ suite "json serialization - serialize":
|
|||
let myobj = MyObj(mystring: "abc", myint: 123, myoption: some true)
|
||||
let mystuint = 100000.u256
|
||||
|
||||
let json = %*{
|
||||
"myobj": myobj,
|
||||
"mystuint": mystuint
|
||||
}
|
||||
let json = %*{"myobj": myobj, "mystuint": mystuint}
|
||||
|
||||
let expected = """{
|
||||
"myobj": {
|
||||
|
@ -72,7 +67,7 @@ suite "json serialization - serialize":
|
|||
myint {.serialize.}: int
|
||||
mybool: bool
|
||||
|
||||
let obj = % MyObj(mystring: "abc", myint: 1, mybool: true)
|
||||
let obj = %MyObj(mystring: "abc", myint: 1, mybool: true)
|
||||
|
||||
let expected = """{
|
||||
"mystring": "abc",
|
||||
|
@ -86,7 +81,7 @@ suite "json serialization - serialize":
|
|||
mystring {.serialize.}: string
|
||||
myint {.serialize.}: int
|
||||
|
||||
let obj = % MyRef(mystring: "abc", myint: 1)
|
||||
let obj = %MyRef(mystring: "abc", myint: 1)
|
||||
|
||||
let expected = """{
|
||||
"mystring": "abc",
|
||||
|
@ -111,9 +106,10 @@ suite "json serialization - serialize":
|
|||
myint {.serialize.}: int
|
||||
|
||||
let obj = MyObj(mystring: "abc", myint: 1)
|
||||
let expected = """{
|
||||
let expected =
|
||||
"""{
|
||||
"mystring": "abc",
|
||||
"myint": 1
|
||||
}"""
|
||||
|
||||
check obj.toJson(pretty=true) == expected
|
||||
check obj.toJson(pretty = true) == expected
|
||||
|
|
|
@ -3,7 +3,6 @@ import std/unittest
|
|||
import pkg/serde
|
||||
|
||||
suite "json serialization, mode = OptIn":
|
||||
|
||||
test "serializes with default mode OptIn when object not marked with serialize":
|
||||
type MyObj = object
|
||||
field1 {.serialize.}: bool
|
||||
|
@ -17,7 +16,7 @@ suite "json serialization, mode = OptIn":
|
|||
field1 {.serialize.}: bool
|
||||
field2: bool
|
||||
|
||||
type MyObjMarked {.serialize(mode=OptIn).} = object
|
||||
type MyObjMarked {.serialize(mode = OptIn).} = object
|
||||
field1 {.serialize.}: bool
|
||||
field2: bool
|
||||
|
||||
|
@ -36,14 +35,12 @@ suite "json serialization, mode = OptIn":
|
|||
test "does not serialize ignored field":
|
||||
type MyObj = object
|
||||
field1 {.serialize.}: bool
|
||||
field2 {.serialize(ignore=true).}: bool
|
||||
field2 {.serialize(ignore = true).}: bool
|
||||
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
check obj.toJson == """{"field1":true}"""
|
||||
|
||||
|
||||
suite "json serialization, mode = OptOut":
|
||||
|
||||
test "serialize on object definition defaults to OptOut mode, serializes all fields":
|
||||
type MyObj {.serialize.} = object
|
||||
field1: bool
|
||||
|
@ -57,7 +54,7 @@ suite "json serialization, mode = OptOut":
|
|||
field1: bool
|
||||
field2: bool
|
||||
|
||||
type MyObjMarked {.serialize(mode=OptOut).} = object
|
||||
type MyObjMarked {.serialize(mode = OptOut).} = object
|
||||
field1: bool
|
||||
field2: bool
|
||||
|
||||
|
@ -67,7 +64,7 @@ suite "json serialization, mode = OptOut":
|
|||
|
||||
test "ignores field when marked with ignore":
|
||||
type MyObj {.serialize.} = object
|
||||
field1 {.serialize(ignore=true).}: bool
|
||||
field1 {.serialize(ignore = true).}: bool
|
||||
field2: bool
|
||||
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
|
@ -81,11 +78,9 @@ suite "json serialization, mode = OptOut":
|
|||
let obj = MyObj(field1: true, field2: true)
|
||||
check obj.toJson == """{"test":true,"field2":true}"""
|
||||
|
||||
|
||||
suite "json serialization - mode = Strict":
|
||||
|
||||
test "serializes all fields in Strict mode":
|
||||
type MyObj {.serialize(mode=Strict).} = object
|
||||
type MyObj {.serialize(mode = Strict).} = object
|
||||
field1: bool
|
||||
field2: bool
|
||||
|
||||
|
@ -93,8 +88,8 @@ suite "json serialization - mode = Strict":
|
|||
check obj.toJson == """{"field1":true,"field2":true}"""
|
||||
|
||||
test "ignores ignored fields in Strict mode":
|
||||
type MyObj {.serialize(mode=Strict).} = object
|
||||
field1 {.serialize(ignore=true).}: bool
|
||||
type MyObj {.serialize(mode = Strict).} = object
|
||||
field1 {.serialize(ignore = true).}: bool
|
||||
field2: bool
|
||||
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
|
|
Loading…
Reference in New Issue