139 lines
4.3 KiB
Nim
139 lines
4.3 KiB
Nim
# json-serialization
|
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
|
# Licensed under either of
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
# at your option.
|
|
# This file may not be copied, modified, or distributed except according to
|
|
# those terms.
|
|
|
|
import
|
|
std/[tables, strutils],
|
|
./types
|
|
|
|
{.push gcsafe, raises: [].}
|
|
|
|
proc len*(n: JsonValueRef): int =
|
|
## If `n` is a `JsonValueKind.Array`, it returns the number of elements.
|
|
## If `n` is a `JsonValueKind.Object`, it returns the number of pairs.
|
|
## Else it returns 0.
|
|
case n.kind
|
|
of JsonValueKind.Array: result = n.arrayVal.len
|
|
of JsonValueKind.Object: result = n.objVal.len
|
|
else: discard
|
|
|
|
proc `[]`*(node: JsonValueRef, name: string): JsonValueRef {.inline.} =
|
|
## Gets a field from a `JsonValueKind.Object`, which must not be nil.
|
|
assert(not isNil(node))
|
|
assert(node.kind == JsonValueKind.Object)
|
|
node.objVal.getOrDefault(name, nil)
|
|
|
|
proc `[]`*(node: JsonValueRef, index: int): JsonValueRef {.inline.} =
|
|
## Gets the node at `index` in an Array. Result is undefined if `index`
|
|
## is out of bounds, but as long as array bound checks are enabled it will
|
|
## result in an exception.
|
|
assert(not isNil(node))
|
|
assert(node.kind == JsonValueKind.Array)
|
|
node.arrayVal[index]
|
|
|
|
proc contains*(node: JsonValueRef, key: string): bool =
|
|
## Checks if `key` exists in `node`.
|
|
assert(node.kind == JsonValueKind.Object)
|
|
node.objVal.hasKey(key)
|
|
|
|
proc contains*(node: JsonValueRef, val: JsonValueRef): bool =
|
|
## Checks if `val` exists in array `node`.
|
|
assert(node.kind == JsonValueKind.Array)
|
|
find(node.arrayVal, val) >= 0
|
|
|
|
proc `[]=`*(obj: JsonValueRef, key: string, val: JsonValueRef) {.inline.} =
|
|
## Sets a field from a `JsonValueKind.Object`.
|
|
assert(obj.kind == JsonValueKind.Object)
|
|
obj.objVal[key] = val
|
|
|
|
proc `[]=`*(obj: JsonValueRef, index: int, val: JsonValueRef) {.inline.} =
|
|
## Sets a field from a `JsonValueKind.Array`.
|
|
assert(obj.kind == JsonValueKind.Array)
|
|
obj.arrayVal[index] = val
|
|
|
|
proc `{}`*(node: JsonValueRef, keys: varargs[string]): JsonValueRef =
|
|
## Traverses the node and gets the given value. If any of the
|
|
## keys do not exist, returns ``nil``. Also returns ``nil`` if one of the
|
|
## intermediate data structures is not an object.
|
|
result = node
|
|
for key in keys:
|
|
if isNil(result) or result.kind != JsonValueKind.Object:
|
|
return nil
|
|
result = result.objVal.getOrDefault(key)
|
|
|
|
proc getOrDefault*(node: JsonValueRef, key: string): JsonValueRef =
|
|
## Gets a field from a `node`. If `node` is nil or not an object or
|
|
## value at `key` does not exist, returns nil
|
|
if not isNil(node) and node.kind == JsonValueKind.Object:
|
|
result = node.objVal.getOrDefault(key)
|
|
|
|
proc delete*(obj: JsonValueRef, key: string) =
|
|
## Deletes ``obj[key]``.
|
|
assert(obj.kind == JsonValueKind.Object)
|
|
if not obj.objVal.hasKey(key):
|
|
raise newException(IndexDefect, "key not in object")
|
|
obj.objVal.del(key)
|
|
|
|
func compare*(lhs, rhs: JsonValueRef): bool
|
|
|
|
func compareObject(lhs, rhs: JsonValueRef): bool =
|
|
## assume lhs.len >= rhs.len
|
|
## null field and no field are treated equals
|
|
for k, v in lhs.objVal:
|
|
let rhsVal = rhs.objVal.getOrDefault(k, nil)
|
|
if rhsVal.isNil:
|
|
if v.kind != JsonValueKind.Null:
|
|
return false
|
|
else:
|
|
continue
|
|
if not compare(rhsVal, v):
|
|
return false
|
|
true
|
|
|
|
func compare*(lhs, rhs: JsonValueRef): bool =
|
|
## The difference between `==` and `compare`
|
|
## lies in the object comparison. Null field `compare`
|
|
## to non existent field will return true.
|
|
## On the other hand, `==` will return false.
|
|
|
|
if lhs.isNil and rhs.isNil:
|
|
return true
|
|
|
|
if not lhs.isNil and rhs.isNil:
|
|
return false
|
|
|
|
if lhs.isNil and not rhs.isNil:
|
|
return false
|
|
|
|
if lhs.kind != rhs.kind:
|
|
return false
|
|
|
|
case lhs.kind
|
|
of JsonValueKind.String:
|
|
lhs.strVal == rhs.strVal
|
|
of JsonValueKind.Number:
|
|
lhs.numVal == rhs.numVal
|
|
of JsonValueKind.Object:
|
|
if lhs.objVal.len >= rhs.objVal.len:
|
|
compareObject(lhs, rhs)
|
|
else:
|
|
compareObject(rhs, lhs)
|
|
of JsonValueKind.Array:
|
|
if lhs.arrayVal.len != rhs.arrayVal.len:
|
|
return false
|
|
for i, x in lhs.arrayVal:
|
|
if not compare(x, rhs.arrayVal[i]):
|
|
return false
|
|
true
|
|
of JsonValueKind.Bool:
|
|
lhs.boolVal == rhs.boolVal
|
|
of JsonValueKind.Null:
|
|
true
|
|
|
|
{.pop.}
|