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