snix/read-tree.nix
Vincent Ambo d23716f354 refactor(read-tree): Keep traversing even if a default.nix is found
This makes it possible for people to drop a default.nix into folders
along the way that add additional things into the attribute set at
that level.

These default.nix files are imported and merged with the rest of the
traversal from that point on. In theory nothing stops a user from
putting a derivation into one of them, but the effects of merging that
derivation's underlying attribute set with random other things from
the traversal are undefined.

This feature is being introduced for a slight revamp of the thirdParty
layout.
2019-12-02 15:05:54 +00:00

79 lines
2 KiB
Nix

initPath: { pkgs, ... } @ args:
let
inherit (builtins)
attrNames
filter
head
isString
length
listToAttrs
map
match
readDir
split
tail
toPath
toString;
attrsToList = attrs: map (name: {
inherit name;
value = attrs."${name}";
}) (attrNames attrs);
isFile = s: s == "regular";
isDir = s: s == "directory";
joinPath = p: f: toPath ((toString p) + "/" + f);
isNixFile = 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) args;
}) 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:
let contents = f path (attrsToList dir);
in if dir ? "default.nix"
then import path (argsWithPath args (pathParts path)) // contents
else contents;
readTree = path: importOr path (readDir path) traverse;
in readTree initPath