summary refs log tree commit diff stats
path: root/lib/std
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/exitprocs.nim22
-rw-r--r--lib/std/isolation.nim31
-rw-r--r--lib/std/jsonutils.nim88
-rw-r--r--lib/std/private/nimbleutils.nim32
4 files changed, 126 insertions, 47 deletions
diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim
index b2811735c..c6537f7f8 100644
--- a/lib/std/exitprocs.nim
+++ b/lib/std/exitprocs.nim
@@ -63,3 +63,25 @@ proc addExitProc*(cl: proc() {.noconv.}) =
   withLock gFunsLock:
     fun()
     gFuns.add Fun(kind: kNoconv, fun2: cl)
+
+when not defined(nimscript):
+  proc getProgramResult*(): int =
+    when defined(js) and defined(nodejs):
+      asm """
+`result` = process.exitCode;
+"""
+    elif not defined(js):
+      result = programResult
+    else:
+      doAssert false
+
+  proc setProgramResult*(a: int) =
+    # pending https://github.com/nim-lang/Nim/issues/14674
+    when defined(js) and defined(nodejs):
+      asm """
+process.exitCode = `a`;
+"""
+    elif not defined(js):
+      programResult = a
+    else:
+      doAssert false
diff --git a/lib/std/isolation.nim b/lib/std/isolation.nim
new file mode 100644
index 000000000..7ca5f0080
--- /dev/null
+++ b/lib/std/isolation.nim
@@ -0,0 +1,31 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2020 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements the `Isolated[T]` type for
+## safe construction of isolated subgraphs that can be
+## passed efficiently to different channels and threads.
+
+type
+  Isolated*[T] = object ## Isolated data can only be moved, not copied.
+    value: T
+
+proc `=`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.}
+
+proc `=sink`*[T](dest: var Isolated[T]; src: Isolated[T]) {.inline.} =
+  # delegate to value's sink operation
+  `=sink`(dest.value, src.value)
+
+proc `=destroy`*[T](dest: var Isolated[T]) {.inline.} =
+  # delegate to value's destroy operation
+  `=destroy`(dest.value)
+
+func isolate*[T](value: sink T): Isolated[T] {.magic: "Isolate".}
+  ## Create an isolated subgraph from the expression `value`.
+  ## Please read https://github.com/nim-lang/RFCs/issues/244
+  ## for more details.
diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim
index be3d7e7c8..22f2a7a89 100644
--- a/lib/std/jsonutils.nim
+++ b/lib/std/jsonutils.nim
@@ -30,10 +30,59 @@ add a way to customize serialization, for eg:
 * handle cyclic references, using a cache of already visited addresses
 ]#
 
+import std/macros
+
 proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
 proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".}
 template distinctBase[T](a: T): untyped = distinctBase(type(a))(a)
 
+macro getDiscriminants(a: typedesc): seq[string] =
+  ## return the discriminant keys
+  # candidate for std/typetraits
+  var a = a.getTypeImpl
+  doAssert a.kind == nnkBracketExpr
+  let sym = a[1]
+  let t = sym.getTypeImpl
+  let t2 = t[2]
+  doAssert t2.kind == nnkRecList
+  result = newTree(nnkBracket)
+  for ti in t2:
+    if ti.kind == nnkRecCase:
+      let key = ti[0][0]
+      let typ = ti[0][1]
+      result.add newLit key.strVal
+  if result.len > 0:
+    result = quote do:
+      @`result`
+  else:
+    result = quote do:
+      seq[string].default
+
+macro initCaseObject(a: typedesc, fun: untyped): untyped =
+  ## does the minimum to construct a valid case object, only initializing
+  ## the discriminant fields; see also `getDiscriminants`
+  # maybe candidate for std/typetraits
+  var a = a.getTypeImpl
+  doAssert a.kind == nnkBracketExpr
+  let sym = a[1]
+  let t = sym.getTypeImpl
+  var t2: NimNode
+  case t.kind
+  of nnkObjectTy: t2 = t[2]
+  of nnkRefTy: t2 = t[0].getTypeImpl[2]
+  else: doAssert false, $t.kind # xxx `nnkPtrTy` could be handled too
+  doAssert t2.kind == nnkRecList
+  result = newTree(nnkObjConstr)
+  result.add sym
+  for ti in t2:
+    if ti.kind == nnkRecCase:
+      let key = ti[0][0]
+      let typ = ti[0][1]
+      let key2 = key.strVal
+      let val = quote do:
+        `fun`(`key2`, typedesc[`typ`])
+      result.add newTree(nnkExprColonExpr, key, val)
+
 proc checkJsonImpl(cond: bool, condStr: string, msg = "") =
   if not cond:
     # just pick 1 exception type for simplicity; other choices would be:
@@ -43,6 +92,19 @@ proc checkJsonImpl(cond: bool, condStr: string, msg = "") =
 template checkJson(cond: untyped, msg = "") =
   checkJsonImpl(cond, astToStr(cond), msg)
 
+template fromJsonFields(a, b, T, keys) =
+  checkJson b.kind == JObject, $(b.kind) # we could customize whether to allow JNull
+  var num = 0
+  for key, val in fieldPairs(a):
+    num.inc
+    when key notin keys:
+      if b.hasKey key:
+        fromJson(val, b[key])
+      else:
+        # we could customize to allow this
+        checkJson false, $($T, key, b)
+  checkJson b.len == num, $(b.len, num, $T, b) # could customize
+
 proc fromJson*[T](a: var T, b: JsonNode) =
   ## inplace version of `jsonTo`
   #[
@@ -85,25 +147,22 @@ proc fromJson*[T](a: var T, b: JsonNode) =
     a.setLen b.len
     for i, val in b.getElems:
       fromJson(a[i], val)
-  elif T is object | tuple:
-    const isNamed = T is object or isNamedTuple(T)
-    when isNamed:
-      checkJson b.kind == JObject, $(b.kind) # we could customize whether to allow JNull
-      var num = 0
-      for key, val in fieldPairs(a):
-        num.inc
-        if b.hasKey key:
-          fromJson(val, b[key])
-        else:
-          # we could customize to allow this
-          checkJson false, $($T, key, b)
-      checkJson b.len == num, $(b.len, num, $T, b) # could customize
+  elif T is object:
+    template fun(key, typ): untyped =
+      jsonTo(b[key], typ)
+    a = initCaseObject(T, fun)
+    const keys = getDiscriminants(T)
+    fromJsonFields(a, b, T, keys)
+  elif T is tuple:
+    when isNamedTuple(T):
+      fromJsonFields(a, b, T, seq[string].default)
     else:
       checkJson b.kind == JArray, $(b.kind) # we could customize whether to allow JNull
       var i = 0
       for val in fields(a):
         fromJson(val, b[i])
         i.inc
+      checkJson b.len == i, $(b.len, i, $T, b) # could customize
   else:
     # checkJson not appropriate here
     static: doAssert false, "not yet implemented: " & $T
@@ -120,8 +179,7 @@ proc toJson*[T](a: T): JsonNode =
     result = newJObject()
     for k, v in pairs(a): result[k] = toJson(v)
   elif T is object | tuple:
-    const isNamed = T is object or isNamedTuple(T)
-    when isNamed:
+    when T is object or isNamedTuple(T):
       result = newJObject()
       for k, v in a.fieldPairs: result[k] = toJson(v)
     else:
diff --git a/lib/std/private/nimbleutils.nim b/lib/std/private/nimbleutils.nim
deleted file mode 100644
index 297fb9695..000000000
--- a/lib/std/private/nimbleutils.nim
+++ /dev/null
@@ -1,32 +0,0 @@
-##[
-internal API for now, API subject to change
-]##
-
-import std/[os,osproc,sugar,strutils]
-
-proc actionRetry*(maxRetry: int, backoffDuration: float, action: proc(): bool): bool =
-  ## retry `action` up to `maxRetry` times with exponential backoff and initial
-  ## duraton of `backoffDuration` seconds
-  var t = backoffDuration
-  for i in 0..<maxRetry:
-    if action(): return true
-    if i == maxRetry - 1: break
-    sleep(int(t * 1000))
-    t = t * 2 # exponential backoff
-  return false
-
-proc nimbleInstall*(name: string, message: var string): bool =
-  let cmd = "nimble install -y " & name
-  let (outp, status) = execCmdEx(cmd)
-  if status != 0:
-    message = "'$1' failed:\n$2" % [cmd, outp]
-    result = false
-  else: result = true
-
-when isMainModule:
-  block:
-    var msg: string
-    let ok = actionRetry(maxRetry = 2, backoffDuration = 0.1):
-      (proc(): bool = nimbleInstall("nonexistant", msg))
-    doAssert "Package not found" in msg
-    doAssert not ok