1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
{ lib, root }:
let
inherit (builtins)
all
attrValues
filter
foldl'
head
length
mapAttrs
readDir
;
inherit (lib)
concatMapAttrs
fix
flatten
flip
getAttrFromPath
isFunction
nameValuePair
optionalAttrs
pipe
remove
take
;
entry = { isDir, path, ... }:
"${if isDir then "directory" else "file"} '${path}'";
view = { cursor ? [ ], node, pov, transformer }:
if node.isDir then
transformer cursor
(flip concatMapAttrs node.children
(name: node: optionalAttrs
{
public = true;
root = pov != "external";
super = pov != "external" && take (length cursor) pov == cursor;
}.${node.visibility}
{
${name} = view {
cursor = cursor ++ [ name ];
inherit node pov transformer;
};
}))
else
node.content;
aggregate = { src, matchers, inputs, tree }:
let
aggregateEntry = path: type:
let
parsed = root.parsePath path type;
inherit (parsed) name visibility stripped;
matches = filter (m: m.matches stripped) matchers;
in
if parsed == null then
null
else if type == "directory" then
nameValuePair name
{
inherit path visibility;
isDir = true;
children = aggregate {
inherit inputs matchers;
src = src + "/${path}";
tree = tree // {
pov = tree.pov ++ [ name ];
};
};
}
else if type == "regular" && matches != [ ] then
nameValuePair name
{
inherit path visibility;
isDir = false;
content = fix (self:
(head matches).loader
(inputs // {
inherit self;
name = name;
povSelf = tree.pov ++ [ name ];
pov = tree.pov;
super = getAttrFromPath tree.pov (view tree);
root = view tree;
})
(src + "/${path}"));
}
else
null;
in
pipe src [
readDir
(mapAttrs aggregateEntry)
attrValues
(remove null)
(flip foldl' { }
(acc: { name, value }:
if acc ? ${name} then
throw ''
haumea failed when traversing ${toString src}
- ${entry acc.${name}} conflicts with ${entry value}
''
else
acc // {
${name} = value;
}))
];
in
{ src
, loader ? root.loaders.default
, inputs ? { }
, transformer ? [ ]
}:
let
transformer' = cursor: flip pipe
(map (t: t cursor) (flatten transformer));
in
assert all
(name: inputs ? ${name}
-> throw "'${name}' cannot be used as the name of an input")
[ "self" "super" "root" "name" "povSelf" "pov" ];
view {
pov = "external";
transformer = transformer';
node = fix (node: {
isDir = true;
children = aggregate {
inherit src inputs;
matchers =
if isFunction loader then
[ (root.matchers.nix loader) ]
else
loader;
tree = {
pov = [ ];
transformer = transformer';
inherit node;
};
};
});
}