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
148
149
|
# nixLib - A library of nix functions for vhack.eu
#
# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
# SPDX-License-Identifier: LGPL-3.0-or-later
#
# This file is part of vhack.eu's nix library.
#
# You should have received a copy of the License along with this program.
# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
{warn}:
# Adapted from this: https://github.com/NixOS/nixpkgs/blob/1814b56453c91192f6d5a6276079948f9fe96c18/pkgs/top-level/by-name-overlay.nix
# This file should not depend on `pkgs` and thus not use `lib`.
{
baseDirectory,
fileName ? null,
fileRegex ? null,
finalizeFunction ? name: value: value,
useShards ? true,
relativePaths ? false,
}:
assert fileName == null -> fileRegex != null;
assert fileRegex == null -> fileName != null; let
finalFileRegex =
if fileRegex == null
then "^${escapeRegex fileName}$"
else fileRegex;
fileDisplay =
if fileName == null
then "matching ${fileRegex}"
else fileName;
# From nixpkgs/lib {{{
# These functions are taken straight out of the `nixpkgs/lib`.
# We can't depended on `pkgs` (and thus on `lib`), because the `pkgs` module argument
# is only defined in the `nixpkgs` module (which is imported through this function).
mapAttrsToList = f: attrs:
builtins.map (name: f name attrs.${name}) (builtins.attrNames attrs);
stringToCharacters = s: builtins.genList (p: builtins.substring p 1 s) (builtins.stringLength s);
escape = list: builtins.replaceStrings list (map (c: "\\${c}") list);
escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
mapAttrs' = f: set: builtins.listToAttrs (map (attr: f attr set.${attr}) (builtins.attrNames set));
nameValuePair = name: value: {inherit name value;};
filterAttrs = pred: set:
builtins.listToAttrs (builtins.concatMap (name: let
v = set.${name};
in
if pred name v
then [(nameValuePair name v)]
else []) (builtins.attrNames set));
# }}}
rawByName = {
baseDirectory,
finalFileRegex,
fileDisplay,
finalizeFunction,
useShards,
relativePaths,
}: let
# Takes a list of attrs as input and returns one merged attr set.
flattenAttrs = list:
if builtins.isList list
then
builtins.foldl' (acc: elem:
if builtins.isList elem
# Merging them with `//` is okay here, as we can be sure that the attr names are
# unique (they were separate dictionary after all)
then acc // (flattenAttrs elem)
else acc // elem) {}
list
else list;
# Module files for a single shard
# Type: String -> String -> ListOf Path
namesForShard = shard: type:
if type != "directory"
then warn "Ignored non-directory, whilst importing by-name directory (${fileDisplay}): '${shard}'" {}
else if useShards
then namesForElementShard shard type
else namesForElementDirect shard type;
# Type: String -> String -> String -> ListOf Path
mkPath = shard: name: toplevelType: let
rawPath = baseDirectory + "/${shard}/${name}";
paths = filterAttrs (_: v: v != null) (mapAttrs' (name: value:
if builtins.match finalFileRegex name != null
then nameValuePair name value
else nameValuePair name null) (builtins.readDir rawPath));
checkPath = pathSegment: type: let
path = rawPath + "/${pathSegment}";
in
if builtins.pathExists path
then
if toplevelType != "directory"
then
# The `namesForShard` function should have already printed a warning.
[null]
else if relativePaths
then pathSegment
else path
else warn "'${builtins.toString path}' does not exist. Skipped" null;
in
if toplevelType != "directory"
then
# The `namesForShard` function should have already printed a warning.
[null]
else if (builtins.attrValues paths) == []
then warn "'${fileDisplay}' did not match anything in ${rawPath}. Skipped" [null]
else mapAttrsToList checkPath paths;
filterNull = list: builtins.filter (value: value != null) list;
# Type: String -> String -> AttrSet
namesForElementShard = shard: _type: (builtins.mapAttrs
(name: type: filterNull (mkPath shard name type))
(builtins.readDir (baseDirectory + "/${shard}")));
# Type: String -> String -> AttrSet
namesForElementDirect = name: type: {"${name}" = filterNull ((mkPath ".") name type);};
# A list of all paths.
output = flattenAttrs (mapAttrsToList namesForShard (builtins.readDir baseDirectory));
in
output;
firstPass = rawByName {
inherit
baseDirectory
finalFileRegex
fileDisplay
finalizeFunction
useShards
relativePaths
;
};
secondPass =
if fileName != null
then
builtins.mapAttrs (name: value: builtins.head value)
(filterAttrs (_: value: value != []) firstPass)
else firstPass;
in
builtins.mapAttrs
finalizeFunction
secondPass
|