about summary refs log tree commit diff
path: root/lib/generators.nix
diff options
context:
space:
mode:
authorMaximilian Bosch <maximilian@mbosch.me>2021-08-26 00:28:49 +0200
committerMaximilian Bosch <maximilian@mbosch.me>2021-08-26 00:28:49 +0200
commit5773ae93f75cfd504d7971c1b26955fa50a00744 (patch)
treed5f19ba068e4efee4f711e1dad969cf3b3c61470 /lib/generators.nix
parentb6d3c9f821b704fbfb68d20a76520fa9e160df36 (diff)
lib/generators: move limit detection into `withRecursion`
As suggested in #131205.

Now it's possible to pretty-print a value with `lib.generators` like
this:

    with lib.generators;
    toPretty { }
      (withRecursion { depthLimit = 10; } /* arbitrarily complex value */)

Also, this can be used for any other pretty-printer now if needed.
Diffstat (limited to 'lib/generators.nix')
-rw-r--r--lib/generators.nix45
1 files changed, 30 insertions, 15 deletions
diff --git a/lib/generators.nix b/lib/generators.nix
index b1639b677f65d..9271b9746aeef 100644
--- a/lib/generators.nix
+++ b/lib/generators.nix
@@ -195,6 +195,30 @@ rec {
     */
   toYAML = {}@args: toJSON args;
 
+  withRecursion =
+    args@{
+      /* If this option is not null, `toPretty` will stop evaluating at a certain depth */
+      depthLimit
+      /* If this option is true, an error will be thrown, if a certain given depth is exceeded */
+    , throwOnDepthLimit ? true
+    }:
+      assert builtins.isInt depthLimit;
+      let
+        transform = depth:
+          if depthLimit != null && depth > depthLimit then
+            if throwOnDepthLimit
+              then throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to pretty-print with `generators.withRecursion'!"
+              else const "<unevaluated>"
+          else id;
+        mapAny = with builtins; depth: v:
+          let
+            evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
+          in
+            if isAttrs v then mapAttrs (const evalNext) v
+            else if isList v then map evalNext v
+            else transform (depth + 1) v;
+      in
+        mapAny 0;
 
   /* Pretty print a value, akin to `builtins.trace`.
     * Should probably be a builtin as well.
@@ -205,23 +229,14 @@ rec {
        (This means fn is type Val -> String.) */
     allowPrettyValues ? false,
     /* If this option is true, the output is indented with newlines for attribute sets and lists */
-    multiline ? true,
-    /* If this option is not null, `toPretty` will stop evaluating at a certain depth */
-    depthLimit ? null,
-    /* If this option is true, an error will be thrown, if a certain given depth is exceeded */
-    throwOnDepthLimit ? false
+    multiline ? true
   }@args:
-    assert depthLimit != null -> builtins.isInt depthLimit;
-    assert throwOnDepthLimit -> depthLimit != null;
     let
-    go = depth: indent: v: with builtins;
+    go = indent: v: with builtins;
     let     isPath   = v: typeOf v == "path";
             introSpace = if multiline then "\n${indent}  " else " ";
             outroSpace = if multiline then "\n${indent}" else " ";
-    in if depthLimit != null && depth > depthLimit then
-      if throwOnDepthLimit then throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to pretty-print with `generators.toPretty'!"
-      else "<unevaluated>"
-    else if   isInt      v then toString v
+    in if   isInt      v then toString v
     else if isFloat    v then "~${toString v}"
     else if isString   v then
       let
@@ -243,7 +258,7 @@ rec {
     else if isList     v then
       if v == [] then "[ ]"
       else "[" + introSpace
-        + libStr.concatMapStringsSep introSpace (go (depth + 1) (indent + "  ")) v
+        + libStr.concatMapStringsSep introSpace (go (indent + "  ")) v
         + outroSpace + "]"
     else if isFunction v then
       let fna = lib.functionArgs v;
@@ -262,10 +277,10 @@ rec {
       else "{" + introSpace
           + libStr.concatStringsSep introSpace (libAttr.mapAttrsToList
               (name: value:
-                "${libStr.escapeNixIdentifier name} = ${go (depth + 1) (indent + "  ") value};") v)
+                "${libStr.escapeNixIdentifier name} = ${go (indent + "  ") value};") v)
         + outroSpace + "}"
     else abort "generators.toPretty: should never happen (v = ${v})";
-  in go 0 "";
+  in go "";
 
   # PLIST handling
   toPlist = {}: v: let