summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/json.nim35
-rw-r--r--tests/stdlib/tjsonmacro.nim105
2 files changed, 132 insertions, 8 deletions
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 67f92dffe..b9279b18c 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -1004,6 +1004,13 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
       exprColonExpr.add(ifStmt)
 
 proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.}
+
+proc detectDistinctType(typeSym: NimNode): NimNode =
+  let
+    typeImpl = getTypeImpl(typeSym)
+    typeInst = getTypeInst(typeSym)
+  result = if typeImpl.typeKind == ntyDistinct: typeImpl else: typeInst
+
 proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
   ## Process a field from a ``RecList``.
   ##
@@ -1022,8 +1029,8 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
     # Add the field value.
     # -> jsonNode["`field`"]
     let indexedJsonNode = createJsonIndexer(jsonNode, $field)
-    exprColonExpr.add(createConstructor(getTypeInst(field), indexedJsonNode))
-
+    let typeNode = detectDistinctType(field)
+    exprColonExpr.add(createConstructor(typeNode, indexedJsonNode))
   of nnkRecCase:
     # A "case" field that introduces a variant.
     let exprColonExpr = newNimNode(nnkExprColonExpr)
@@ -1248,7 +1255,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
       let seqT = typeSym[1]
       let forLoopI = genSym(nskForVar, "i")
       let indexerNode = createJsonIndexer(jsonNode, forLoopI)
-      let constructorNode = createConstructor(seqT, indexerNode)
+      let constructorNode = createConstructor(detectDistinctType(seqT), indexerNode)
 
       # Create a statement expression containing a for loop.
       result = quote do:
@@ -1284,7 +1291,10 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
 
     # Handle all other types.
     let obj = getType(typeSym)
-    if obj.kind == nnkBracketExpr:
+    let typeNode = getTypeImpl(typeSym)
+    if typeNode.typeKind == ntyDistinct:
+      result = createConstructor(typeNode, jsonNode)
+    elif obj.kind == nnkBracketExpr:
       # When `Sym "Foo"` turns out to be a `ref object`.
       result = createConstructor(obj, jsonNode)
     else:
@@ -1295,6 +1305,21 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
     # TODO: The fact that `jsonNode` here works to give a good line number
     # is weird. Specifying typeSym should work but doesn't.
     error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode)
+  of nnkDistinctTy:
+    var baseType = typeSym
+    # solve nested distinct types
+    while baseType.typeKind == ntyDistinct:
+      let impl = getTypeImpl(baseType[0])
+      if impl.typeKind != ntyDistinct:
+        baseType = baseType[0]
+        break
+      baseType = impl
+    let ret = createConstructor(baseType, jsonNode)
+    let typeInst = getTypeInst(typeSym)
+    result = quote do:
+      (
+        `typeInst`(`ret`)
+      )
   else:
     doAssert false, "Unable to create constructor for: " & $typeSym.kind
 
@@ -1418,7 +1443,7 @@ macro to*(node: JsonNode, T: typedesc): untyped =
   ##     doAssert data.person.age == 21
   ##     doAssert data.list == @[1, 2, 3, 4]
 
-  let typeNode = getTypeInst(T)
+  let typeNode = getTypeImpl(T)
   expectKind(typeNode, nnkBracketExpr)
   doAssert(($typeNode[0]).normalize == "typedesc")
 
diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim
index f13d2e5cb..c0b4d5f78 100644
--- a/tests/stdlib/tjsonmacro.nim
+++ b/tests/stdlib/tjsonmacro.nim
@@ -337,7 +337,7 @@ when isMainModule:
         n2: Option[int]
         n3: Option[string]
         n4: Option[bool]
-        
+
     var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""")
     let j0Deser = j0.to(Obj)
     doAssert j0Deser.n1 == 1
@@ -411,10 +411,109 @@ when isMainModule:
     doAssert dataDeser.a == 1
     doAssert dataDeser.f == 6
     doAssert dataDeser.i == 9.9'f32
-  
+
   # deserialize directly into a table
   block:
     let s = """{"a": 1, "b": 2}"""
     let t = parseJson(s).to(Table[string, int])
     doAssert t["a"] == 1
-    doAssert t["b"] == 2
\ No newline at end of file
+    doAssert t["b"] == 2
+
+  block:
+    # bug #8037
+    type
+      Apple = distinct string
+      String = distinct Apple
+      Email = distinct string
+      MyList = distinct seq[int]
+      MyYear = distinct Option[int]
+      MyTable = distinct Table[string, int]
+      MyArr = distinct array[3, float]
+      MyRef = ref object
+        name: string
+      MyObj = object
+        color: int
+      MyDistRef = distinct MyRef
+      MyDistObj = distinct MyObj
+      Toot = object
+        name*: String
+        email*: Email
+        list: MyList
+        year: MyYear
+        dict: MyTable
+        arr: MyArr
+        person: MyDistRef
+        distfruit: MyDistObj
+        dog: MyRef
+        fruit: MyObj
+        emails: seq[String]
+
+    var tJson = parseJson("""
+      {
+        "name":"Bongo",
+        "email":"bongo@bingo.com",
+        "list": [11,7,15],
+        "year": 1975,
+        "dict": {"a": 1, "b": 2},
+        "arr": [1.0, 2.0, 7.0],
+        "person": {"name": "boney"},
+        "dog": {"name": "honey"},
+        "fruit": {"color": 10},
+        "distfruit": {"color": 11},
+        "emails": ["abc", "123"]
+      }
+    """)
+
+    var t = to(tJson, Toot)
+    doAssert string(t.name) == "Bongo"
+    doAssert string(t.email) == "bongo@bingo.com"
+    doAssert seq[int](t.list) == @[11,7,15]
+    doAssert Option[int](t.year).get() == 1975
+    doAssert Table[string,int](t.dict)["a"] == 1
+    doAssert Table[string,int](t.dict)["b"] == 2
+    doAssert array[3, float](t.arr) == [1.0,2.0,7.0]
+    doAssert MyRef(t.person).name == "boney"
+    doAssert MyObj(t.distFruit).color == 11
+    doAssert t.dog.name == "honey"
+    doAssert t.fruit.color == 10
+    doAssert seq[string](t.emails) == @["abc", "123"]
+
+    block test_table:
+      var y = parseJson("""{"a": 1, "b": 2, "c": 3}""")
+      var u = y.to(MyTable)
+      var v = y.to(Table[string, int])
+      doAssert Table[string, int](u)["a"] == 1
+      doAssert Table[string, int](u)["b"] == 2
+      doAssert Table[string, int](u)["c"] == 3
+      doAssert v["a"] == 1
+
+    block primitive_string:
+      const kApple = "apple"
+      var u = newJString(kApple)
+      var v = u.to(Email)
+      var w = u.to(Apple)
+      var x = u.to(String)
+      doAssert string(v) == kApple
+      doAssert string(w) == kApple
+      doAssert string(x) == kApple
+
+    block test_option:
+      var u = newJInt(1137)
+      var v = u.to(MyYear)
+      var w = u.to(Option[int])
+      doAssert Option[int](v).get() == 1137
+      doAssert w.get() == 1137
+
+    block test_object:
+      var u = parseJson("""{"color": 987}""")
+      var v = u.to(MyObj)
+      var w = u.to(MyDistObj)
+      doAssert v.color == 987
+      doAssert MyObj(w).color == 987
+
+    block test_ref_object:
+      var u = parseJson("""{"name": "smith"}""")
+      var v = u.to(MyRef)
+      var w = u.to(MyDistRef)
+      doAssert v.name == "smith"
+      doAssert MyRef(w).name == "smith"