···9 split
10 match
11 typeOf
012 ;
1314 inherit (lib.lists)
···23 take
24 drop
25 ;
002627 inherit (lib.strings)
28 concatStringsSep
···119 if base == dirOf base then { root = base; inherit components; }
120 else recurse ([ (baseNameOf base) ] ++ components) (dirOf base);
121 in recurse [];
0000000000000000000000122123in /* No rec! Add dependencies on this file at the top. */ {
124···320 root = deconstructed.root;
321 subpath = joinRelPath deconstructed.components;
322 };
00000000000000000000000000000000000000000000000000000000323324 /*
325 Whether a value is a valid subpath string.
···9 split
10 match
11 typeOf
12+ storeDir
13 ;
1415 inherit (lib.lists)
···24 take
25 drop
26 ;
27+28+ listHasPrefix = lib.lists.hasPrefix;
2930 inherit (lib.strings)
31 concatStringsSep
···122 if base == dirOf base then { root = base; inherit components; }
123 else recurse ([ (baseNameOf base) ] ++ components) (dirOf base);
124 in recurse [];
125+126+ # The components of the store directory, typically [ "nix" "store" ]
127+ storeDirComponents = splitRelPath ("./" + storeDir);
128+ # The number of store directory components, typically 2
129+ storeDirLength = length storeDirComponents;
130+131+ # Type: [ String ] -> Bool
132+ #
133+ # Whether path components have a store path as a prefix, according to
134+ # https://nixos.org/manual/nix/stable/store/store-path.html#store-path.
135+ componentsHaveStorePathPrefix = components:
136+ # path starts with the store directory (typically /nix/store)
137+ listHasPrefix storeDirComponents components
138+ # is not the store directory itself, meaning there's at least one extra component
139+ && storeDirComponents != components
140+ # and the first component after the store directory has the expected format.
141+ # NOTE: We could change the hash regex to be [0-9a-df-np-sv-z],
142+ # because these are the actual ASCII characters used by Nix's base32 implementation,
143+ # but this is not fully specified, so let's tie this too much to the currently implemented concept of store paths.
144+ # Similar reasoning applies to the validity of the name part.
145+ # We care more about discerning store path-ness on realistic values. Making it airtight would be fragile and slow.
146+ && match ".{32}-.+" (elemAt components storeDirLength) != null;
147148in /* No rec! Add dependencies on this file at the top. */ {
149···345 root = deconstructed.root;
346 subpath = joinRelPath deconstructed.components;
347 };
348+349+ /*
350+ Whether a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path)
351+ has a [store path](https://nixos.org/manual/nix/stable/store/store-path.html#store-path)
352+ as a prefix.
353+354+ :::{.note}
355+ As with all functions of this `lib.path` library, it does not work on paths in strings,
356+ which is how you'd typically get store paths.
357+358+ Instead, this function only handles path values themselves,
359+ which occur when Nix files in the store use relative path expressions.
360+ :::
361+362+ Type:
363+ hasStorePathPrefix :: Path -> Bool
364+365+ Example:
366+ # Subpaths of derivation outputs have a store path as a prefix
367+ hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo/bar/baz
368+ => true
369+370+ # The store directory itself is not a store path
371+ hasStorePathPrefix /nix/store
372+ => false
373+374+ # Derivation outputs are store paths themselves
375+ hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo
376+ => true
377+378+ # Paths outside the Nix store don't have a store path prefix
379+ hasStorePathPrefix /home/user
380+ => false
381+382+ # Not all paths under the Nix store are store paths
383+ hasStorePathPrefix /nix/store/.links/10gg8k3rmbw8p7gszarbk7qyd9jwxhcfq9i6s5i0qikx8alkk4hq
384+ => false
385+386+ # Store derivations are also store paths themselves
387+ hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo.drv
388+ => true
389+ */
390+ hasStorePathPrefix = path:
391+ let
392+ deconstructed = deconstructPath path;
393+ in
394+ assert assertMsg
395+ (isPath path)
396+ "lib.path.hasStorePathPrefix: Argument is of type ${typeOf path}, but a path was expected";
397+ assert assertMsg
398+ # This function likely breaks or needs adjustment if used with other filesystem roots, if they ever get implemented.
399+ # Let's try to error nicely in such a case, though it's unclear how an implementation would work even and whether this could be detected.
400+ # See also https://github.com/NixOS/nix/pull/6530#discussion_r1422843117
401+ (deconstructed.root == /. && toString deconstructed.root == "/")
402+ "lib.path.hasStorePathPrefix: Argument has a filesystem root (${toString deconstructed.root}) that's not /, which is currently not supported.";
403+ componentsHaveStorePathPrefix deconstructed.components;
404405 /*
406 Whether a value is a valid subpath string.
+29-1
lib/path/tests/unit.nix
···3{ libpath }:
4let
5 lib = import libpath;
6- inherit (lib.path) hasPrefix removePrefix append splitRoot subpath;
00078 cases = lib.runTests {
9 # Test examples from the lib.path.append documentation
···89 testSplitRootExample4 = {
90 expr = (builtins.tryEval (splitRoot "/foo/bar")).success;
91 expected = false;
000000000000000000000000092 };
9394 # Test examples from the lib.path.subpath.isValid documentation