1# Functions for querying information about the filesystem
2# without copying any files to the Nix store.
3{ lib }:
4
5# Tested in lib/tests/filesystem.sh
6let
7 inherit (builtins)
8 readDir
9 pathExists
10 ;
11
12 inherit (lib.strings)
13 hasPrefix
14 ;
15
16 inherit (lib.filesystem)
17 pathType
18 ;
19in
20
21{
22
23 /*
24 The type of a path. The path needs to exist and be accessible.
25 The result is either "directory" for a directory, "regular" for a regular file, "symlink" for a symlink, or "unknown" for anything else.
26
27 Type:
28 pathType :: Path -> String
29
30 Example:
31 pathType /.
32 => "directory"
33
34 pathType /some/file.nix
35 => "regular"
36 */
37 pathType =
38 builtins.readFileType or
39 # Nix <2.14 compatibility shim
40 (path:
41 if ! pathExists path
42 # Fail irrecoverably to mimic the historic behavior of this function and
43 # the new builtins.readFileType
44 then abort "lib.filesystem.pathType: Path ${toString path} does not exist."
45 # The filesystem root is the only path where `dirOf / == /` and
46 # `baseNameOf /` is not valid. We can detect this and directly return
47 # "directory", since we know the filesystem root can't be anything else.
48 else if dirOf path == path
49 then "directory"
50 else (readDir (dirOf path)).${baseNameOf path}
51 );
52
53 /*
54 Whether a path exists and is a directory.
55
56 Type:
57 pathIsDirectory :: Path -> Bool
58
59 Example:
60 pathIsDirectory /.
61 => true
62
63 pathIsDirectory /this/does/not/exist
64 => false
65
66 pathIsDirectory /some/file.nix
67 => false
68 */
69 pathIsDirectory = path:
70 pathExists path && pathType path == "directory";
71
72 /*
73 Whether a path exists and is a regular file, meaning not a symlink or any other special file type.
74
75 Type:
76 pathIsRegularFile :: Path -> Bool
77
78 Example:
79 pathIsRegularFile /.
80 => false
81
82 pathIsRegularFile /this/does/not/exist
83 => false
84
85 pathIsRegularFile /some/file.nix
86 => true
87 */
88 pathIsRegularFile = path:
89 pathExists path && pathType path == "regular";
90
91 /*
92 A map of all haskell packages defined in the given path,
93 identified by having a cabal file with the same name as the
94 directory itself.
95
96 Type: Path -> Map String Path
97 */
98 haskellPathsInDir =
99 # The directory within to search
100 root:
101 let # Files in the root
102 root-files = builtins.attrNames (builtins.readDir root);
103 # Files with their full paths
104 root-files-with-paths =
105 map (file:
106 { name = file; value = root + "/${file}"; }
107 ) root-files;
108 # Subdirectories of the root with a cabal file.
109 cabal-subdirs =
110 builtins.filter ({ name, value }:
111 builtins.pathExists (value + "/${name}.cabal")
112 ) root-files-with-paths;
113 in builtins.listToAttrs cabal-subdirs;
114 /*
115 Find the first directory containing a file matching 'pattern'
116 upward from a given 'file'.
117 Returns 'null' if no directories contain a file matching 'pattern'.
118
119 Type: RegExp -> Path -> Nullable { path : Path; matches : [ MatchResults ]; }
120 */
121 locateDominatingFile =
122 # The pattern to search for
123 pattern:
124 # The file to start searching upward from
125 file:
126 let go = path:
127 let files = builtins.attrNames (builtins.readDir path);
128 matches = builtins.filter (match: match != null)
129 (map (builtins.match pattern) files);
130 in
131 if builtins.length matches != 0
132 then { inherit path matches; }
133 else if path == /.
134 then null
135 else go (dirOf path);
136 parent = dirOf file;
137 isDir =
138 let base = baseNameOf file;
139 type = (builtins.readDir parent).${base} or null;
140 in file == /. || type == "directory";
141 in go (if isDir then file else parent);
142
143
144 /*
145 Given a directory, return a flattened list of all files within it recursively.
146
147 Type: Path -> [ Path ]
148 */
149 listFilesRecursive =
150 # The path to recursively list
151 dir:
152 lib.flatten (lib.mapAttrsToList (name: type:
153 if type == "directory" then
154 lib.filesystem.listFilesRecursive (dir + "/${name}")
155 else
156 dir + "/${name}"
157 ) (builtins.readDir dir));
158
159}