From b42e178ed4ba33925a474644f5bc54723b2ff15f Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Wed, 19 Jul 2023 16:20:40 +0200 Subject: [PATCH 1/2] lib.path.splitRoot: init Co-authored-by: Robert Hensing --- lib/path/default.nix | 46 +++++++++++++++++++++++++++++++++++++++++ lib/path/tests/unit.nix | 19 ++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/lib/path/default.nix b/lib/path/default.nix index 3a871bc05283c..1300f0f09011d 100644 --- a/lib/path/default.nix +++ b/lib/path/default.nix @@ -271,6 +271,52 @@ 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. + - `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" + => + */ + 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; + }; + /* Whether a value is a valid subpath string. - The value is a string diff --git a/lib/path/tests/unit.nix b/lib/path/tests/unit.nix index 3e4b216f099ff..8bfb6f201219f 100644 --- a/lib/path/tests/unit.nix +++ b/lib/path/tests/unit.nix @@ -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 @@ -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; + }; + # Test examples from the lib.path.subpath.isValid documentation testSubpathIsValidExample1 = { expr = subpath.isValid null; From d7bf0d777a0edba5a6c87d59e9b7516c796fcd1f Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Thu, 20 Jul 2023 22:10:39 +0200 Subject: [PATCH 2/2] lib.path.subpath.isValid: Add definition of a subpath Co-authored-by: Robert Hensing --- lib/path/default.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/path/default.nix b/lib/path/default.nix index 1300f0f09011d..24a7f85affc1d 100644 --- a/lib/path/default.nix +++ b/lib/path/default.nix @@ -319,6 +319,9 @@ in /* No rec! Add dependencies on this file at the top. */ { /* 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