From 056a78e95f6b5e8a3303d60d73836f328f57f569 Mon Sep 17 00:00:00 2001 From: munna0908 Date: Wed, 28 May 2025 11:49:50 +0530 Subject: [PATCH] refactor: reimplement CBOR deserialization with type-based fromCbor interface --- serde/cbor/deserializer.nim | 220 +++++++++++++++++++++++++--------- serde/cbor/helpers.nim | 24 +--- serde/cbor/jsonhook.nim | 10 +- serde/cbor/serializer.nim | 14 ++- serde/cbor/types.nim | 3 +- serde/utils/pragmas.nim | 15 ++- tests/cbor/testObjects.nim | 28 ++--- tests/cbor/testPrimitives.nim | 2 +- 8 files changed, 210 insertions(+), 106 deletions(-) diff --git a/serde/cbor/deserializer.nim b/serde/cbor/deserializer.nim index 92928d1..0b3432d 100644 --- a/serde/cbor/deserializer.nim +++ b/serde/cbor/deserializer.nim @@ -1,16 +1,20 @@ # This file is a modified version of Emery Hemingway’s CBOR library for Nim, # originally available at https://github.com/ehmry/cbor-nim and released under The Unlicense. -import std/[math, streams, options, tables, strutils, times, typetraits] +import std/[math, streams, options, tables, strutils, times, typetraits, macros] import ./types import ./helpers -import ../utils/types +import ../utils/types as utilsTypes +import ../utils/pragmas import ./errors import pkg/questionable import pkg/questionable/results export results export types +export pragmas +export utilsTypes + {.push raises: [].} @@ -95,7 +99,6 @@ proc nextUInt*(c: var CborParser): ?!BiggestUInt = ?c.next() return success(val) - proc nextInt*(c: var CborParser): ?!BiggestInt = ## Parse the integer value that the parser is positioned on. var val: BiggestInt @@ -146,7 +149,7 @@ proc nextBytes*(c: var CborParser; buf: var openArray[byte]): ?!void = let n = c.s.readData(buf[0].addr, buf.len) if n != buf.len: return failure(newCborError("truncated read of CBOR data")) - tryNext(c) + ?c.next() success() except OSError as e: return failure(e.msg) @@ -178,14 +181,13 @@ proc nextText*(c: var CborParser; buf: var string): ?!void = let n = c.s.readData(buf[0].addr, buf.len) if n != buf.len: return failure(newCborError("truncated read of CBOR data")) - tryNext(c) + ?c.next() success() except IOError as e: return failure(e.msg) except OSError as e: return failure(e.msg) - proc nextText*(c: var CborParser): ?!string = ## Read the text that the parser is positioned on into a string and advance. var buf: string @@ -195,20 +197,26 @@ proc nextText*(c: var CborParser): ?!string = return success(buf) -func arrayLen*(c: CborParser): int = +func arrayLen*(c: CborParser): ?!int = ## Return the length of the array that the parser is positioned on. - assert(c.kind == CborEventKind.cborArray, $c.kind) - c.intVal.int + if c.kind != CborEventKind.cborArray: + return failure(newCborError("Expected array, got " & $c.kind)) -func mapLen*(c: CborParser): int = + return success(c.intVal.int) + +func mapLen*(c: CborParser): ?!int = ## Return the length of the map that the parser is positioned on. - assert(c.kind == CborEventKind.cborMap, $c.kind) - c.intVal.int + if c.kind != CborEventKind.cborMap: + return failure(newCborError("Expected map, got " & $c.kind)) -func tag*(c: CborParser): uint64 = + return success(c.intVal.int) + +func tag*(c: CborParser): ?!uint64 = ## Return the tag value the parser is positioned on. - assert(c.kind == CborEventKind.cborTag, $c.kind) - c.intVal + if c.kind != CborEventKind.cborTag: + return failure(newCborError("Expected tag, got " & $c.kind)) + + return success(c.intVal) proc skipNode*(c: var CborParser): ?!void = ## Skip the item the parser is positioned on. @@ -221,7 +229,7 @@ proc skipNode*(c: var CborParser): ?!void = return c.next() of CborEventKind.cborBytes, CborEventKind.cborText: if c.isIndefinite: - tryNext(c) + ?c.next() while c.kind != CborEventKind.cborBreak: if c.kind != CborEventKind.cborBytes: return failure(newCborError("expected bytes, got " & $c.kind)) @@ -232,28 +240,28 @@ proc skipNode*(c: var CborParser): ?!void = return c.next() of CborEventKind.cborArray: if c.isIndefinite: - tryNext(c) + ?c.next() while c.kind != CborEventKind.cborBreak: - trySkip(c) + ?c.skipNode() return c.next() else: let len = c.intVal - tryNext(c) + ?c.next() for i in 1..len: - trySkip(c) + ?c.skipNode() of CborEventKind.cborMap: let mapLen = c.intVal.int if c.isIndefinite: - tryNext(c) + ?c.next() while c.kind != CborEventKind.cborBreak: - trySkip(c) + ?c.skipNode() return c.next() else: - tryNext(c) + ?c.next() for _ in 1 .. mapLen: - trySkip(c) + ?c.skipNode() of CborEventKind.cborTag: - tryNext(c) + ?c.next() return c.skipNode() of CborEventKind.cborFloat: without f =? c.nextFloat(), error: @@ -279,14 +287,14 @@ proc nextNode*(c: var CborParser): ?!CborNode = return failure(newCborError("end of CBOR stream")) of CborEventKind.cborPositive: next = CborNode(kind: cborUnsigned, uint: c.intVal) - tryNext(c) + ?c.next() of CborEventKind.cborNegative: next = CborNode(kind: cborNegative, int: -1 - c.intVal.int64) - tryNext(c) + ?c.next() of CborEventKind.cborBytes: if c.isIndefinite: next = CborNode(kind: cborBytes, bytes: newSeq[byte]()) - tryNext(c) + ?c.next() while c.kind != CborEventKind.cborBreak: if c.kind != CborEventKind.cborBytes: return failure(newCborError("Expected bytes, got " & $c.kind)) @@ -297,7 +305,7 @@ proc nextNode*(c: var CborParser): ?!CborNode = let n = c.s.readData(next.bytes[pos].addr, chunkLen) if n != chunkLen: return failure(newCborError("truncated read of CBOR data")) - tryNext(c) + ?c.next() else: without rawBytes =? c.nextBytes(), error: return failure(error) @@ -305,7 +313,7 @@ proc nextNode*(c: var CborParser): ?!CborNode = of CborEventKind.cborText: if c.isIndefinite: next = CborNode(kind: cborText, text: "") - tryNext(c) + ?c.next() while c.kind != CborEventKind.cborBreak: if c.kind != CborEventKind.cborText: return failure(newCborError("Expected text, got " & $c.kind)) @@ -316,8 +324,8 @@ proc nextNode*(c: var CborParser): ?!CborNode = let n = c.s.readData(next.text[pos].addr, chunkLen) if n != chunkLen: return failure(newCborError("truncated read of CBOR data")) - tryNext(c) - tryNext(c) + ?c.next() + ?c.next() else: without text =? c.nextText(), error: return failure(error) @@ -325,14 +333,14 @@ proc nextNode*(c: var CborParser): ?!CborNode = of CborEventKind.cborArray: next = CborNode(kind: cborArray, seq: newSeq[CborNode](c.intVal)) if c.isIndefinite: - tryNext(c) + ?c.next() while c.kind != CborEventKind.cborBreak: without node =? c.nextNode(), error: return failure(error) next.seq.add(node) - tryNext(c) + ?c.next() else: - tryNext(c) + ?c.next() for i in 0..next.seq.high: without node =? c.nextNode(), error: return failure(error) @@ -342,16 +350,16 @@ proc nextNode*(c: var CborParser): ?!CborNode = next = CborNode(kind: cborMap, map: initOrderedTable[CborNode, CborNode]( mapLen.nextPowerOfTwo)) if c.isIndefinite: - tryNext(c) + ?c.next() while c.kind != CborEventKind.cborBreak: without key =? c.nextNode(), error: return failure(error) without val =? c.nextNode(), error: return failure(error) next.map[key] = val - tryNext(c) + ?c.next() else: - tryNext(c) + ?c.next() for _ in 1 .. mapLen: without key =? c.nextNode(), error: return failure(error) @@ -360,7 +368,7 @@ proc nextNode*(c: var CborParser): ?!CborNode = next.map[key] = val of CborEventKind.cborTag: let tag = c.intVal - tryNext(c) + ?c.next() without node =? c.nextNode(), error: return failure(error) next = node @@ -371,7 +379,7 @@ proc nextNode*(c: var CborParser): ?!CborNode = next = CborNode(kind: cborSimple, simple: c.intVal.uint8) else: next = CborNode(kind: cborSimple, simple: c.minor) - tryNext(c) + ?c.next() of CborEventKind.cborFloat: without f =? c.nextFloat(), error: return failure(error) @@ -383,15 +391,17 @@ proc nextNode*(c: var CborParser): ?!CborNode = return failure(e.msg) except IOError as e: return failure(e.msg) - except Exception as e: + except CatchableError as e: return failure(e.msg) + except Exception as e: + raise newException(Defect, e.msg, e) proc readCbor*(s: Stream): ?!CborNode = ## Parse a stream into a CBOR object. var parser: CborParser parser.open(s) - tryNext(parser) + ?parser.next() parser.nextNode() proc parseCbor*(s: string): ?!CborNode = @@ -473,7 +483,7 @@ proc getInt*(n: CborNode; default: int = 0): int = else: default proc parseDateText(n: CborNode): DateTime {.raises: [TimeParseError].} = - parse(n.text, timeFormat) + parse(n.text, dateTimeFormat) proc parseTime(n: CborNode): Time = case n.kind @@ -557,7 +567,6 @@ proc getSigned*(n: CborNode; default: int64 = 0): int64 = of cborNegative: n.int else: default - func getFloat*(n: CborNode; default = 0.0): float = ## Get the floating-poing value of a ``CborNode`` or a fallback. if n.kind == cborFloat: @@ -565,12 +574,69 @@ func getFloat*(n: CborNode; default = 0.0): float = else: default +proc fromCbor*[T: distinct](_: type T; n: CborNode): ?!T = + success T(?T.distinctBase.fromCbor(n)) + +proc fromCbor*[T: SomeUnsignedInt](_: type T; n: CborNode): ?!T = + expectCborKind(T, {cborUnsigned}, n) + var v = T(n.uint) + if v.BiggestUInt == n.uint: + return success(v) + else: + return failure(newCborError("Value overflow for unsigned integer")) + +proc fromCbor*[T: SomeSignedInt](_: type T; n: CborNode): ?!T = + expectCborKind(T, {cborUnsigned, cborNegative}, n) + if n.kind == cborUnsigned: + var v = T(n.uint) + if v.BiggestUInt == n.uint: + return success(v) + else: + return failure(newCborError("Value overflow for signed integer")) + elif n.kind == cborNegative: + var v = T(n.int) + if v.BiggestInt == n.int: + return success(v) + else: + return failure(newCborError("Value overflow for signed integer")) + +proc fromCbor*[T: SomeFloat](_: type T; n: CborNode): ?!T = + expectCborKind(T, {cborFloat}, n) + return success(T(n.float)) + +proc fromCbor*(_: type seq[byte]; n: CborNode): ?!seq[byte] = + expectCborKind(seq[byte], cborBytes, n) + return success(n.bytes) + +proc fromCbor*(_: type string; n: CborNode): ?!string = + expectCborKind(string, cborText, n) + return success(n.text) + +proc fromCbor*(_: type bool; n: CborNode): ?!bool = + if not n.isBool: + return failure(newCborError("Expected boolean, got " & $n.kind)) + return success(n.getBool) + +proc fromCbor*[T](_: type seq[T]; n: CborNode): ?!seq[T] = + expectCborKind(seq[T], cborArray, n) + var arr = newSeq[T](n.seq.len) + for i, elem in n.seq: + arr[i] = ?T.fromCbor(elem) + success arr + +proc fromCbor*[T: tuple](_: type T; n: CborNode): ?!T = + expectCborKind(T, cborArray, n) + var res = T.default + if n.seq.len != T.tupleLen: + return failure(newCborError("Expected tuple of length " & $T.tupleLen)) + var i: int + for f in fields(res): + f = ?typeof(f).fromCbor(n.seq[i]) + inc i + + success res proc fromCbor*[T](v: var T; n: CborNode): ?!void = - ## Return a Result containing the value if `v` can be converted from a given `CborNode`, - ## or an error if conversion fails. - ## Can be extended and overriden with `fromCborHook(v: var T; n: CborNode)` - ## for specific types of `T`. try: when T is CborNode: v = n @@ -580,14 +646,14 @@ proc fromCbor*[T](v: var T; n: CborNode): ?!void = elif T is distinct: return fromCbor(distinctBase v, n) elif T is SomeUnsignedInt: - exceptCborKind(T, {cborUnsigned}, n) + expectCborKind(T, {cborUnsigned}, n) v = T n.uint if v.BiggestUInt == n.uint: return success() else: return failure(newCborError("Value overflow for unsigned integer")) elif T is SomeSignedInt: - exceptCborKind(T, {cborUnsigned, cborNegative}, n) + expectCborKind(T, {cborUnsigned, cborNegative}, n) if n.kind == cborUnsigned: v = T n.uint if v.BiggestUInt == n.uint: @@ -606,19 +672,19 @@ proc fromCbor*[T](v: var T; n: CborNode): ?!void = v = n.getBool return success() elif T is SomeFloat: - exceptCborKind(T, {cborFloat}, n) + expectCborKind(T, {cborFloat}, n) v = T n.float return success() elif T is seq[byte]: - exceptCborKind(T, {cborBytes}, n) + expectCborKind(T, {cborBytes}, n) v = n.bytes return success() elif T is string: - exceptCborKind(T, {cborText}, n) + expectCborKind(T, {cborText}, n) v = n.text return success() elif T is seq: - exceptCborKind(T, {cborArray}, n) + expectCborKind(T, {cborArray}, n) v.setLen n.seq.len for i, e in n.seq: let itemResult = fromCbor(v[i], e) @@ -627,7 +693,7 @@ proc fromCbor*[T](v: var T; n: CborNode): ?!void = return failure(itemResult.error) return success() elif T is tuple: - exceptCborKind(T, {cborArray}, n) + expectCborKind(T, {cborArray}, n) if n.seq.len != T.tupleLen: return failure(newCborError("Expected tuple of length " & $T.tupleLen)) var i: int @@ -645,7 +711,7 @@ proc fromCbor*[T](v: var T; n: CborNode): ?!void = if isNil(v): new(v) return fromCbor(v[], n) elif T is object: - exceptCborKind(T, {cborMap}, n) + expectCborKind(T, {cborMap}, n) var i: int key = CborNode(kind: cborText) @@ -664,5 +730,45 @@ proc fromCbor*[T](v: var T; n: CborNode): ?!void = return failure(newCborError("Extra fields in map")) else: return failure(newCborError("Unsupported type: " & $T)) + except CatchableError as e: + return failure newCborError(e.msg) except Exception as e: - return failure(newCborError(e.msg)) + raise newException(Defect, e.msg, e) + +proc fromCbor*[T: ref object or object](_: type T; n: CborNode): ?!T = + when T is CborNode: + return success T(n) + + expectCborKind(T, {cborMap}, n) + + var res = + when type(T) is ref: + T.new() + else: + T.default + + try: + var + i: int + key = CborNode(kind: cborText) + for name, value in fieldPairs( + when type(T) is ref: + res[] + else: + res + ): + key.text = name + + if not n.map.hasKey key: + return failure(newCborError("Missing field: " & name)) + else: + value = ?typeof(value).fromCbor(n.map[key]) + inc i + if i == n.map.len: + return success(res) + else: + return failure(newCborError("Extra fields in map")) + except CatchableError as e: + return failure newCborError(e.msg) + except Exception as e: + raise newException(Defect, e.msg, e) diff --git a/serde/cbor/helpers.nim b/serde/cbor/helpers.nim index 4e32ee5..4332dab 100644 --- a/serde/cbor/helpers.nim +++ b/serde/cbor/helpers.nim @@ -5,40 +5,28 @@ import ./types import ./errors from macros import newDotExpr, newIdentNode, strVal - -template tryNext*(c: var CborParser) = - let nextRes = c.next() - if nextRes.isFailure: - return failure(nextRes.error) - -template trySkip*(c: var CborParser) = - let skipRes = c.skipNode() - if skipRes.isFailure: - return failure(skipRes.error) - -template exceptCborKind*(expectedType: type, expectedKinds: set[CborNodeKind], +template expectCborKind*(expectedType: type, expectedKinds: set[CborNodeKind], cbor: CborNode) = if cbor.kind notin expectedKinds: return failure(newUnexpectedKindError(expectedType, expectedKinds, cbor)) -template exceptCborKind*(expectedType: type, expectedKind: CborNodeKind, +template expectCborKind*(expectedType: type, expectedKind: CborNodeKind, cbor: CborNode) = - exceptCborKind(expectedType, {expectedKind}, cbor) + expectCborKind(expectedType, {expectedKind}, cbor) -template exceptCborKind*(expectedType: type, expectedKinds: set[CborEventKind], +template expectCborKind*(expectedType: type, expectedKinds: set[CborEventKind], cbor: CborNode) = if cbor.kind notin expectedKinds: return failure(newUnexpectedKindError(expectedType, expectedKinds, cbor)) -template exceptCborKind*(expectedType: type, expectedKind: CborEventKind, +template expectCborKind*(expectedType: type, expectedKind: CborEventKind, cbor: CborNode) = - exceptCborKind(expectedType, {expectedKind}, cbor) + expectCborKind(expectedType, {expectedKind}, cbor) macro dot*(obj: object, fld: string): untyped = ## Turn ``obj.dot("fld")`` into ``obj.fld``. newDotExpr(obj, newIdentNode(fld.strVal)) - func floatSingle*(half: uint16): float32 = ## Convert a 16-bit float to 32-bits. func ldexp(x: float64; exponent: int): float64 {.importc: "ldexp", diff --git a/serde/cbor/jsonhook.nim b/serde/cbor/jsonhook.nim index 6486225..faccd1a 100644 --- a/serde/cbor/jsonhook.nim +++ b/serde/cbor/jsonhook.nim @@ -7,7 +7,7 @@ import ./types import ./errors import ./deserializer -proc toJsonHook*(n: CborNode): JsonNode = +proc toJson*(n: CborNode): JsonNode = case n.kind: of cborUnsigned: newJInt n.uint.BiggestInt @@ -20,15 +20,15 @@ proc toJsonHook*(n: CborNode): JsonNode = of cborArray: let a = newJArray() for e in n.seq.items: - a.add(e.toJsonHook) + a.add(e.toJson) a of cborMap: let o = newJObject() for k, v in n.map.pairs: if k.kind == cborText: - o[k.text] = v.toJsonHook + o[k.text] = v.toJson else: - o[$k] = v.toJsonHook + o[$k] = v.toJson o of cborTag: nil of cborSimple: @@ -42,4 +42,4 @@ proc toJsonHook*(n: CborNode): JsonNode = of cborRaw: without parsed =? parseCbor(n.raw), error: raise newCborError(error.msg) - toJsonHook(parsed) + toJson(parsed) diff --git a/serde/cbor/serializer.nim b/serde/cbor/serializer.nim index d605b85..2c0b014 100644 --- a/serde/cbor/serializer.nim +++ b/serde/cbor/serializer.nim @@ -303,19 +303,22 @@ proc sort*(n: var CborNode): ?!void = for key, val in n.map.mpairs: without res =? key.toRaw, error: return failure(error) - tmp[res] = move(val) + if tmp.hasKey(res): + tmp[res] = move(val) sort(tmp) do (x, y: tuple[k: CborNode; v: CborNode]) -> int: result = cmp(x.k.raw, y.k.raw) n.map = move tmp success() - except Exception as e: + except CatchableError as e: return failure(e.msg) + except Exception as e: + raise newException(Defect, e.msg, e) proc writeCborHook*(str: Stream; dt: DateTime): ?!void = ## Write a `DateTime` using the tagged string representation ## defined in RCF7049 section 2.4.1. ?writeCborTag(str, 0) - ?writeCbor(str, format(dt, timeFormat)) + ?writeCbor(str, format(dt, dateTimeFormat)) success() proc writeCborHook*(str: Stream; t: Time): ?!void = @@ -345,8 +348,10 @@ func toCbor*(x: openArray[CborNode]): ?!CborNode = func toCbor*(pairs: openArray[(CborNode, CborNode)]): ?!CborNode = try: return success(CborNode(kind: cborMap, map: pairs.toOrderedTable)) - except Exception as e: + except CatchableError as e: return failure(e.msg) + except Exception as e: + raise newException(Defect, e.msg, e) func toCbor*(tag: uint64; val: CborNode): ?!CborNode = without res =? toCbor(val), error: @@ -367,7 +372,6 @@ func toCbor*(x: SomeFloat): ?!CborNode = func toCbor*(x: pointer): ?!CborNode = ## A hack to produce a CBOR null item. - assert(x.isNil) if not x.isNil: return failure("pointer is not nil") success(CborNode(kind: cborSimple, simple: 22)) diff --git a/serde/cbor/types.nim b/serde/cbor/types.nim index 560cefa..c7e3214 100644 --- a/serde/cbor/types.nim +++ b/serde/cbor/types.nim @@ -3,7 +3,8 @@ import std/[streams, tables, options, hashes, times] -const timeFormat* = initTimeFormat "yyyy-MM-dd'T'HH:mm:sszzz" +# This format is defined in RCF8949 section 3.4.1. +const dateTimeFormat* = initTimeFormat "yyyy-MM-dd'T'HH:mm:sszzz" const PositiveMajor* = 0'u8 diff --git a/serde/utils/pragmas.nim b/serde/utils/pragmas.nim index e6dede9..69d243e 100644 --- a/serde/utils/pragmas.nim +++ b/serde/utils/pragmas.nim @@ -6,8 +6,10 @@ export types {.push raises: [].} -template serialize*(key = "", ignore = false, mode = SerdeMode.OptOut) {.pragma.} -template deserialize*(key = "", ignore = false, mode = SerdeMode.OptOut) {.pragma.} +template serialize*(key = "", ignore = false, + mode = SerdeMode.OptOut) {.pragma.} +template deserialize*(key = "", ignore = false, + mode = SerdeMode.OptOut) {.pragma.} proc isDefault[T](paramValue: T): bool {.compileTime.} = when T is SerdeMode: @@ -23,7 +25,8 @@ template expectMissingPragmaParam*(value, pragma, name, msg) = if paramName == name and not paramValue.isDefault: raiseAssert(msg) -template getSerdeFieldOptions*(pragma, fieldName, fieldValue): SerdeFieldOptions = +template getSerdeFieldOptions*(pragma, fieldName, + fieldValue): SerdeFieldOptions = var opts = SerdeFieldOptions(key: fieldName, ignore: false) when fieldValue.hasCustomPragma(pragma): fieldValue.expectMissingPragmaParam( @@ -43,12 +46,14 @@ template getSerdeMode*(T, pragma): SerdeMode = T.expectMissingPragmaParam( pragma, "key", - "Cannot set " & astToStr(pragma) & " 'key' on '" & $T & "' type definition.", + "Cannot set " & astToStr(pragma) & " 'key' on '" & $T & + "' type definition.", ) T.expectMissingPragmaParam( pragma, "ignore", - "Cannot set " & astToStr(pragma) & " 'ignore' on '" & $T & "' type definition.", + "Cannot set " & astToStr(pragma) & " 'ignore' on '" & $T & + "' type definition.", ) let (_, _, mode) = T.getCustomPragmaVal(pragma) mode diff --git a/tests/cbor/testObjects.nim b/tests/cbor/testObjects.nim index 2f8415e..d9f9f17 100644 --- a/tests/cbor/testObjects.nim +++ b/tests/cbor/testObjects.nim @@ -24,7 +24,7 @@ type s: string nums: seq[int] - CompositeNested = object + CompositeNested {.deserialize.} = object u: uint64 n: int b: seq[byte] @@ -37,15 +37,16 @@ type coordinates: tuple[x: int, y: int, label: string] refInner: ref Inner -proc fromCborHook*(v: var CustomColor, n: CborNode): ?!void = +proc fromCbor*(_: type CustomColor, n: CborNode): ?!CustomColor = + var v: CustomColor if n.kind == cborNegative: v = CustomColor(n.int) - result = success() + success(v) else: - result = failure(newSerdeError("Expected signed integer, got " & $n.kind)) + failure(newSerdeError("Expected signed integer, got " & $n.kind)) # Custom fromCborHook for CustomPoint -proc fromCborHook*(v: var CustomPoint, n: CborNode): ?!void = +proc fromCbor*(_: type CustomPoint, n: CborNode): ?!CustomPoint = if n.kind == cborArray and n.seq.len == 2: var x, y: int let xResult = fromCbor(x, n.seq[0]) @@ -56,10 +57,9 @@ proc fromCborHook*(v: var CustomPoint, n: CborNode): ?!void = if yResult.isFailure: return failure(yResult.error) - v = CustomPoint(x: x, y: y) - result = success() + return success(CustomPoint(x: x, y: y)) else: - result = failure(newSerdeError("Expected array of length 2 for CustomPoint")) + return failure(newSerdeError("Expected array of length 2 for CustomPoint")) # Helper function to create CBOR data for testing proc createPointCbor(x, y: int): CborNode = @@ -97,10 +97,11 @@ suite "CBOR deserialization": let node = createObjectCbor("Test Object", point, Green) # Deserialize - var deserializedObj: CustomObject + # var deserializedObj: CustomObject # Check result - let result = fromCbor(deserializedObj, node) + let result = CustomObject.fromCbor(node) check result.isSuccess + var deserializedObj = result.tryValue check deserializedObj.name == "Test Object" check deserializedObj.point.x == 15 check deserializedObj.point.y == 25 @@ -148,10 +149,9 @@ suite "CBOR deserialization": let node = parseResult.tryValue # Deserialize to CompositeNested object - var roundtrip: CompositeNested - let deserResult = fromCbor(roundtrip, node) - check deserResult.isSuccess - + let res = CompositeNested.fromCbor(node) + check res.isSuccess + let roundtrip = res.tryValue # Check top-level fields check roundtrip.u == original.u check roundtrip.n == original.n diff --git a/tests/cbor/testPrimitives.nim b/tests/cbor/testPrimitives.nim index 2741390..5cf8044 100644 --- a/tests/cbor/testPrimitives.nim +++ b/tests/cbor/testPrimitives.nim @@ -27,7 +27,7 @@ suite "decode": controlCbor = base64.decode v["cbor"].getStr without c =? parseCbor(controlCbor), error: fail() - let js = c.toJsonHook() + let js = c.toJson() if js.isNil: fail() else: