summary refs log tree commit diff
path: root/lib/fileset
diff options
context:
space:
mode:
authorSilvan Mosberger <silvan.mosberger@tweag.io>2023-09-21 01:00:59 +0200
committerSilvan Mosberger <silvan.mosberger@tweag.io>2023-10-04 16:28:55 +0200
commitefbcf5938fca42f004e34c3f56f9ab1a12a95559 (patch)
tree3a4e9c86eb023d033e170ec83c7c52ef17f96d45 /lib/fileset
parent86802e19ceffbdff6a2a80dbec2d5f14f9f40efa (diff)
downloadnixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar
nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar.gz
nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar.bz2
nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar.lz
nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar.xz
nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.tar.zst
nixpkgs-efbcf5938fca42f004e34c3f56f9ab1a12a95559.zip
lib.fileset: Add internal helpers for pretty-printing
Diffstat (limited to 'lib/fileset')
-rw-r--r--lib/fileset/internal.nix109
1 files changed, 107 insertions, 2 deletions
diff --git a/lib/fileset/internal.nix b/lib/fileset/internal.nix
index ad10b9ac0bb..ebfe69b010d 100644
--- a/lib/fileset/internal.nix
+++ b/lib/fileset/internal.nix
@@ -7,11 +7,14 @@ let
     isString
     pathExists
     readDir
-    typeOf
+    seq
     split
+    trace
+    typeOf
     ;
 
   inherit (lib.attrsets)
+    attrNames
     attrValues
     mapAttrs
     setAttrByPath
@@ -241,7 +244,7 @@ rec {
       // value;
 
   /*
-    A normalisation of a filesetTree suitable for use in builtins.path-based filtering:
+    A normalisation of a filesetTree suitable filtering with `builtins.path`:
     - Replace all directories that have no files with `null`.
       This removes directories that would be empty
     - Replace all directories with all files with `"directory"`.
@@ -270,6 +273,108 @@ rec {
     else
       tree;
 
+  /*
+    A minimal normalisation of a filesetTree, intended for pretty-printing:
+    - If all children of a path are recursively included or empty directories, the path itself is also recursively included
+    - If all children of a path are fully excluded or empty directories, the path itself is an empty directory
+    - Other empty directories are represented with the special "emptyDir" string
+      While these could be replaced with `null`, that would take another mapAttrs
+
+    Note that this function is partially lazy.
+
+    Type: Path -> filesetTree -> filesetTree (with "emptyDir"'s)
+  */
+  _normaliseTreeMinimal = path: tree:
+    if tree == "directory" || isAttrs tree then
+      let
+        entries = _directoryEntries path tree;
+        normalisedSubtrees = mapAttrs (name: _normaliseTreeMinimal (path + "/${name}")) entries;
+        subtreeValues = attrValues normalisedSubtrees;
+      in
+      # If there are no entries, or all entries are empty directories, return "emptyDir".
+      # After this branch we know that there's at least one file
+      if all (value: value == "emptyDir") subtreeValues then
+        "emptyDir"
+
+      # If all subtrees are fully included or empty directories
+      # (both of which are coincidentally represented as strings), return "directory".
+      # This takes advantage of the fact that empty directories can be represented as included directories.
+      # Note that the tree == "directory" check allows avoiding recursion
+      else if tree == "directory" || all (value: isString value) subtreeValues then
+        "directory"
+
+      # If all subtrees are fully excluded or empty directories, return null.
+      # This takes advantage of the fact that empty directories can be represented as excluded directories
+      else if all (value: isNull value || value == "emptyDir") subtreeValues then
+        null
+
+      # Mix of included and excluded entries
+      else
+        normalisedSubtrees
+    else
+      tree;
+
+  # Trace a filesetTree in a pretty way when the resulting value is evaluated.
+  # This can handle both normal filesetTree's, and ones returned from _normaliseTreeMinimal
+  # Type: Path -> filesetTree (with "emptyDir"'s) -> Null
+  _printMinimalTree = base: tree:
+    let
+      treeSuffix = tree:
+        if isAttrs tree then
+          ""
+        else if tree == "directory" then
+          " (all files in directory)"
+        else
+          # This does "leak" the file type strings of the internal representation,
+          # but this is the main reason these file type strings even are in the representation!
+          # TODO: Consider removing that information from the internal representation for performance.
+          # The file types can still be printed by querying them only during tracing
+          " (${tree})";
+
+      # Only for attribute set trees
+      traceTreeAttrs = prevLine: indent: tree:
+        foldl' (prevLine: name:
+          let
+            subtree = tree.${name};
+
+            # Evaluating this prints the line for this subtree
+            thisLine =
+              trace "${indent}- ${name}${treeSuffix subtree}" prevLine;
+          in
+          if subtree == null || subtree == "emptyDir" then
+            # Don't print anything at all if this subtree is empty
+            prevLine
+          else if isAttrs subtree then
+            # A directory with explicit entries
+            # Do print this node, but also recurse
+            traceTreeAttrs thisLine "${indent}  " subtree
+          else
+            # Either a file, or a recursively included directory
+            # Do print this node but no further recursion needed
+            thisLine
+        ) prevLine (attrNames tree);
+
+      # Evaluating this will print the first line
+      firstLine =
+        if tree == null || tree == "emptyDir" then
+          trace "(empty)" null
+        else
+          trace "${toString base}${treeSuffix tree}" null;
+    in
+    if isAttrs tree then
+      traceTreeAttrs firstLine "" tree
+    else
+      firstLine;
+
+  # Pretty-print a file set in a pretty way when the resulting value is evaluated
+  # Type: fileset -> Null
+  _printFileset = fileset:
+    if fileset._internalIsEmptyWithoutBase then
+      trace "(empty)" null
+    else
+      _printMinimalTree fileset._internalBase
+        (_normaliseTreeMinimal fileset._internalBase fileset._internalTree);
+
   # Turn a fileset into a source filter function suitable for `builtins.path`
   # Only directories recursively containing at least one files are recursed into
   # Type: Path -> fileset -> (String -> String -> Bool)