79 lines
2.4 KiB
Nix
79 lines
2.4 KiB
Nix
{ lib }:
|
|
let
|
|
inherit (lib) filter head mapAttrs tail;
|
|
|
|
# absolutePathComponentsBetween : PathOrString -> PathOrString -> [String]
|
|
# absolutePathComponentsBetween ancestor descendant
|
|
#
|
|
# Returns the path components that form the path from ancestor to descendant.
|
|
# Will not return ".." components, which is a feature. Throws when ancestor
|
|
# and descendant arguments aren't in said relation to each other.
|
|
#
|
|
# Example:
|
|
#
|
|
# absolutePathComponentsBetween /a /a/b/c == ["b" "c"]
|
|
# absolutePathComponentsBetween /a/b/c /a/b/c == []
|
|
absolutePathComponentsBetween =
|
|
ancestor: descendant:
|
|
let
|
|
a' = /. + ancestor;
|
|
go = d:
|
|
if a' == d
|
|
then []
|
|
else if d == /.
|
|
then throw "absolutePathComponentsBetween: path ${toString ancestor} is not an ancestor of ${toString descendant}"
|
|
else go (dirOf d) ++ [(baseNameOf d)];
|
|
in
|
|
go (/. + descendant);
|
|
|
|
/*
|
|
Memoize a function that takes a path argument.
|
|
Example:
|
|
analyzeTree = dir:
|
|
let g = memoizePathFunction (p: t: expensiveFunction p t) (p: {}) dir;
|
|
in presentExpensiveData g;
|
|
Type:
|
|
memoizePathFunction :: (Path -> Type -> a) -> (Path -> a) -> Path -> (Path -> a)
|
|
*/
|
|
memoize =
|
|
# Function to memoize
|
|
f:
|
|
# What to return when a path does not exist, as a function of the path
|
|
missing:
|
|
# Filesystem location below which the returned function is defined. `/.` may be acceptable, but a path closer to the data of interest is better.
|
|
root:
|
|
|
|
let
|
|
makeTree = dir: type: {
|
|
value = f dir type;
|
|
inherit type;
|
|
children =
|
|
if type == "directory"
|
|
then mapAttrs
|
|
(key: type: makeTree (dir + "/${key}") type)
|
|
(builtins.readDir dir)
|
|
else {};
|
|
};
|
|
|
|
# This is where the memoization happens
|
|
tree = makeTree root (
|
|
# We can't query the type of a store path in Nix without readFileType in
|
|
# pure mode, so we assume.
|
|
"directory"
|
|
);
|
|
|
|
lookup = notFound: list: subtree:
|
|
if list == []
|
|
then subtree.value
|
|
else if subtree.children ? ${head list}
|
|
then lookup notFound (tail list) subtree.children.${head list}
|
|
else notFound;
|
|
in
|
|
path: lookup
|
|
(missing path)
|
|
(absolutePathComponentsBetween root path)
|
|
tree;
|
|
in
|
|
{
|
|
inherit memoize absolutePathComponentsBetween;
|
|
}
|