dream2nix/lib/internal/prepareSourceTree.nix

127 lines
3.1 KiB
Nix

{lib, ...}: let
l = builtins // lib;
/*
Transforms a source tree into a nix attrset for simplicity and performance.
It's simpler to traverse an attrset than having to readDir manually.
It's more performant because it allows to parse a json or toml by accessing
attributes like `.jsonContent` or `.tomlContent` which ensures that the file
is only parsed once, even if the parsed content is used in multiple places.
produces this structure:
{
files = {
"package.json" = {
relPath = "package.json"
fullPath = "${source}/package.json"
content = ;
jsonContent = ;
tomlContent = ;
}
};
directories = {
"packages" = {
relPath = "packages";
fullPath = "${source}/packages";
files = {
};
directories = {
};
};
};
}
*/
prepareSourceTree = {
source,
depth ? 10,
}:
prepareSourceTreeInternal source "" "" depth;
readTextFile = file: l.replaceStrings ["\r\n"] ["\n"] (l.readFile file);
prepareSourceTreeInternal = sourceRoot: relPath: name: depth: let
relPath' = relPath;
fullPath' = "${toString sourceRoot}/${relPath}";
current = l.readDir fullPath';
fileNames =
l.filterAttrs (n: v: v == "regular") current;
directoryNames =
l.filterAttrs (n: v: v == "directory") current;
makeNewPath = prefix: name:
if prefix == ""
then name
else "${prefix}/${name}";
directories =
l.mapAttrs
(dname: _:
prepareSourceTreeInternal
sourceRoot
(makeNewPath relPath dname)
dname
(depth - 1))
directoryNames;
files =
l.mapAttrs
(fname: _: rec {
name = fname;
fullPath = "${fullPath'}/${fname}";
relPath = makeNewPath relPath' fname;
content = readTextFile fullPath;
jsonContent = l.fromJSON content;
tomlContent = l.fromTOML content;
})
fileNames;
# returns the tree object of the given sub-path
getNodeFromPath = path: let
cleanPath = l.removePrefix "/" path;
pathSplit = l.splitString "/" cleanPath;
dirSplit = l.init pathSplit;
leaf = l.last pathSplit;
error = throw ''
Failed while trying to navigate to ${path} from ${fullPath'}
'';
dirAttrPath =
l.init
(l.concatMap
(x: [x] ++ ["directories"])
dirSplit);
dir =
if (l.length dirSplit == 0) || dirAttrPath == [""]
then self
else if ! l.hasAttrByPath dirAttrPath directories
then error
else l.getAttrFromPath dirAttrPath directories;
in
if path == ""
then self
else if dir ? directories."${leaf}"
then dir.directories."${leaf}"
else if dir ? files."${leaf}"
then dir.files."${leaf}"
else error;
self =
{
inherit files getNodeFromPath name relPath;
fullPath = fullPath';
}
# stop recursion if depth is reached
// (l.optionalAttrs (depth > 0) {
inherit directories;
});
in
self;
in
prepareSourceTree