Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib.path.splitRoot: init #244358

Merged
merged 2 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions lib/path/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,57 @@ in /* No rec! Add dependencies on this file at the top. */ {
second argument: "${toString path2}" with root "${toString path2Deconstructed.root}"'';
joinRelPath components;

/*
Split the filesystem root from a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path).
The result is an attribute set with these attributes:
- `root`: The filesystem root of the path, meaning that this directory has no parent directory.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unfortunate that this concept does not have Nix documentation yet.

(no action required now)

- `subpath`: The [normalised subpath string](#function-library-lib.path.subpath.normalise) that when [appended](#function-library-lib.path.append) to `root` returns the original path.

Laws:
- [Appending](#function-library-lib.path.append) the `root` and `subpath` gives the original path:

p ==
append
(splitRoot p).root
(splitRoot p).subpath

- Trying to get the parent directory of `root` using [`readDir`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readDir) returns `root` itself:

dirOf (splitRoot p).root == (splitRoot p).root

Type:
splitRoot :: Path -> { root :: Path, subpath :: String }

Example:
splitRoot /foo/bar
=> { root = /.; subpath = "./foo/bar"; }

splitRoot /.
=> { root = /.; subpath = "./."; }

# Nix neutralises `..` path components for all path values automatically
splitRoot /foo/../bar
=> { root = /.; subpath = "./bar"; }

splitRoot "/foo/bar"
=> <error>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reader will wonder what's the problem with this? Surely / is the root.

I think it might be sensible to treat all "${storeDir}/${x}" as roots, but that's a discussion for another time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That feels like something that should be supported by lib.strings.* instead. The error could even redirect the user to that

*/
roberth marked this conversation as resolved.
Show resolved Hide resolved
splitRoot = path:
assert assertMsg
(isPath path)
"lib.path.splitRoot: Argument is of type ${typeOf path}, but a path was expected";
let
deconstructed = deconstructPath path;
in {
root = deconstructed.root;
subpath = joinRelPath deconstructed.components;
};
fricklerhandwerk marked this conversation as resolved.
Show resolved Hide resolved

/* Whether a value is a valid subpath string.

A subpath string points to a specific file or directory within an absolute base directory.
It is a stricter form of a relative path that excludes `..` components, since those could escape the base directory.

- The value is a string

- The string is not empty
Expand Down
19 changes: 18 additions & 1 deletion lib/path/tests/unit.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{ libpath }:
let
lib = import libpath;
inherit (lib.path) hasPrefix removePrefix append subpath;
inherit (lib.path) hasPrefix removePrefix append splitRoot subpath;

cases = lib.runTests {
# Test examples from the lib.path.append documentation
Expand Down Expand Up @@ -74,6 +74,23 @@ let
expected = "./foo";
};

testSplitRootExample1 = {
expr = splitRoot /foo/bar;
expected = { root = /.; subpath = "./foo/bar"; };
};
testSplitRootExample2 = {
expr = splitRoot /.;
expected = { root = /.; subpath = "./."; };
};
testSplitRootExample3 = {
expr = splitRoot /foo/../bar;
expected = { root = /.; subpath = "./bar"; };
};
testSplitRootExample4 = {
expr = (builtins.tryEval (splitRoot "/foo/bar")).success;
expected = false;
};

infinisil marked this conversation as resolved.
Show resolved Hide resolved
# Test examples from the lib.path.subpath.isValid documentation
testSubpathIsValidExample1 = {
expr = subpath.isValid null;
Expand Down