blob: 066d3268aac4ad1019b5ed9d2323eb2f808dfb49 [file] [log] [blame]
Serge Bazanski2efb6982020-10-03 00:13:45 +02001# The MIT License (MIT)
2#
3# Copyright (c) 2019 Vincent Ambo
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal
7# in the Software without restriction, including without limitation the rights
8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9# copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
22
23{ ... }:
24
25args: initPath:
26
27let
28 inherit (builtins)
29 attrNames
30 baseNameOf
31 filter
32 hasAttr
33 head
34 isAttrs
35 length
36 listToAttrs
37 map
38 match
39 readDir
40 substring;
41
42 argsWithPath = parts:
43 let meta.locatedAt = parts;
44 in meta // (if isAttrs args then args else args meta);
45
46 readDirVisible = path:
47 let
48 children = readDir path;
49 isVisible = f: f == ".skip-subtree" || (substring 0 1 f) != ".";
50 names = filter isVisible (attrNames children);
51 in listToAttrs (map (name: {
52 inherit name;
53 value = children.${name};
54 }) names);
55
56 # The marker is added to every set that was imported directly by
57 # readTree.
58 importWithMark = path: parts:
59 let imported = import path (argsWithPath parts);
60 in if (isAttrs imported)
61 then imported // { __readTree = true; }
62 else imported;
63
64 nixFileName = file:
65 let res = match "(.*)\.nix" file;
66 in if res == null then null else head res;
67
68 readTree = path: parts:
69 let
70 dir = readDirVisible path;
71 self = importWithMark path parts;
72 joinChild = c: path + ("/" + c);
73
74 # Import subdirectories of the current one, unless the special
75 # `.skip-subtree` file exists which makes readTree ignore the
76 # children.
77 #
78 # This file can optionally contain information on why the tree
79 # should be ignored, but its content is not inspected by
80 # readTree
81 filterDir = f: dir."${f}" == "directory";
82 children = if hasAttr ".skip-subtree" dir then [] else map (c: {
83 name = c;
84 value = readTree (joinChild c) (parts ++ [ c ]);
85 }) (filter filterDir (attrNames dir));
86
87 # Import Nix files
88 nixFiles = filter (f: f != null) (map nixFileName (attrNames dir));
89 nixChildren = map (c: let p = joinChild (c + ".nix"); in {
90 name = c;
91 value = importWithMark p (parts ++ [ c ]);
92 }) nixFiles;
93 in if dir ? "default.nix"
94 then (if isAttrs self then self // (listToAttrs children) else self)
95 else listToAttrs (nixChildren ++ children);
96in readTree initPath [ (baseNameOf initPath) ]