1
0
Fork 0
hscloud/nix/readtree/default.nix

117 lines
3.3 KiB
Nix
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Copyright (c) 2019 Vincent Ambo
# Copyright (c) 2020-2021 The TVL Authors
# SPDX-License-Identifier: MIT
#
# Provides a function to automatically read a a filesystem structure
# into a Nix attribute set.
#
# Optionally accepts an argument `argsFilter` on import, which is a
# function that receives the current tree location (as a list of
# strings) and the argument set and can arbitrarily modify it.
{ argsFilter ? (x: _parts: x)
, ... }:
let
inherit (builtins)
attrNames
baseNameOf
concatStringsSep
filter
hasAttr
head
isAttrs
length
listToAttrs
map
match
readDir
substring;
assertMsg = pred: msg:
if pred
then true
else builtins.trace msg false;
argsWithPath = args: parts:
let meta.locatedAt = parts;
in meta // (if isAttrs args then args else args meta);
readDirVisible = path:
let
children = readDir path;
isVisible = f: f == ".skip-subtree" || (substring 0 1 f) != ".";
names = filter isVisible (attrNames children);
in listToAttrs (map (name: {
inherit name;
value = children.${name};
}) names);
# Create a mark containing the location of this attribute.
marker = parts: {
__readTree = parts;
};
# The marker is added to every set that was imported directly by
# readTree.
importWithMark = args: path: parts:
let
importedFile = import path;
pathType = builtins.typeOf importedFile;
imported =
assert assertMsg
(pathType == "lambda")
"readTree: trying to import ${toString path}, but its a ${pathType}, you need to make it a function like { depot, pkgs, ... }";
importedFile (argsFilter (argsWithPath args parts) parts);
in if (isAttrs imported)
then imported // (marker parts)
else imported;
nixFileName = file:
let res = match "(.*)\\.nix" file;
in if res == null then null else head res;
readTree = { args, initPath, rootDir, parts }:
let
dir = readDirVisible initPath;
joinChild = c: initPath + ("/" + c);
self = if rootDir
then { __readTree = []; }
else importWithMark args initPath parts;
# Import subdirectories of the current one, unless the special
# `.skip-subtree` file exists which makes readTree ignore the
# children.
#
# This file can optionally contain information on why the tree
# should be ignored, but its content is not inspected by
# readTree
filterDir = f: dir."${f}" == "directory";
children = if hasAttr ".skip-subtree" dir then [] else map (c: {
name = c;
value = readTree {
args = args;
initPath = (joinChild c);
rootDir = false;
parts = (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 = importWithMark args p (parts ++ [ c ]);
}) nixFiles;
in if dir ? "default.nix"
then (if isAttrs self then self // (listToAttrs children) else self)
else (listToAttrs (nixChildren ++ children) // (marker parts));
in {
__functor = _: args: initPath: readTree {
inherit args initPath;
rootDir = true;
parts = [];
};
}