about summary refs log tree commit diff
path: root/read-tree.nix
diff options
context:
space:
mode:
Diffstat (limited to 'read-tree.nix')
-rw-r--r--read-tree.nix95
1 files changed, 30 insertions, 65 deletions
diff --git a/read-tree.nix b/read-tree.nix
index fd72c2619cfc..a4c0e354ce72 100644
--- a/read-tree.nix
+++ b/read-tree.nix
@@ -1,84 +1,49 @@
-# TODO(tazjin): avoid {} by only calling functions *after* checking what they are
-
 args: initPath:
 
 let
   inherit (builtins)
     attrNames
+    baseNameOf
     filter
     head
-    isString
     length
     listToAttrs
     map
     match
-    readDir
-    split
-    tail
-    toString;
-
-  attrsToList = attrs: map (name: {
-    inherit name;
-    value = attrs."${name}";
-  }) (attrNames attrs);
+    readDir;
 
-  isFile = s: s == "regular";
-  isDir = s: s == "directory";
+  argsWithPath = parts: args // {
+    locatedAt = parts;
+  };
 
-  joinPath = p: f: p + ("/" + f);
+  # The marker is added to everything that was imported directly by
+  # readTree.
+  marker = { __readTree = true; };
 
-  isNixFile = file:
+  nixFileName = file:
     let res = match "(.*)\.nix" file;
     in if res == null then null else head res;
 
-  filterNixFiles = dir:
-    let files = filter (e: isFile e.value && e.name != "default.nix") dir;
-        nixFiles = map (f: {
-          # Name and value are intentionally flipped to get the
-          # correct attribute set structure back out
-          name = isNixFile f.name;
-          value = f.name; # i.e. the path
-        }) files;
-    in filter (f: isString f.name) nixFiles;
-
-  # Some packages require that their position in the tree is passed in
-  # as an argument. To do this the root directory (i.e. $PWD during
-  # imports) is chopped off the front of the path components in
-  # imports.
-  pathParts = p: tail (filter isString (split "/" (toString p)));
-  initLen = length (pathParts ./.);
-  drop = n: l:
-    if n == 0
-      then l
-      else if l == []
-        then []
-        else drop (n - 1) (tail l);
-
-  argsWithPath = args: parts: args // {
-    locatedAt = drop initLen parts;
-  };
-
-  traverse = path: dir:
-    let nixFiles = filterNixFiles dir;
-        imported = map (f: {
-          inherit (f) name;
-          value = import (joinPath path f.value) (argsWithPath args (pathParts path));
-        }) nixFiles;
-        dirs = map (d: {
-          inherit (d) name;
-          value = readTree (joinPath path d.name);
-        }) (filter (e: isDir e.value) dir);
-    in listToAttrs (imported ++ dirs);
-
-  importOr = path: dir: f:
+  readTree = path: parts:
     let
-      allContents = f path (attrsToList dir);
-      dirOnlyContents = f path (filter (f: f.value == "directory") (attrsToList dir));
+      dir = readDir path;
+      self = (import path (argsWithPath parts)) // marker;
+      joinChild = c: path + ("/" + c);
+
+      # Import non-empty subdirectories
+      filterDir = f: dir."${f}" == "directory";
+      children = map (c: {
+        name = c;
+        value = readTree (joinChild c) (parts ++ [ c ]);
+      }) (filter filterDir (attrNames dir));
+
+      # Import Nix files
+      nixFiles = filter (f: f != null) (map nixFileName (attrNames dir));
+      nixChildren = map (c: let p = joinChild (c + ".nix"); in {
+        name = c;
+        value = (import p (argsWithPath (parts ++ [ c ]))) // marker;
+      }) nixFiles;
     in if dir ? "default.nix"
-      then import path (argsWithPath args (pathParts path))
-        // { __treeChildren = true; } # used downstream for traversals
-        // dirOnlyContents
-      else allContents;
-
-  readTree = path: importOr path (readDir path) traverse;
-in readTree initPath
+      then self // (listToAttrs children)
+      else listToAttrs (nixChildren ++ children);
+in readTree initPath [ (baseNameOf initPath) ]