lib.path.subpath.isValid: init

The first path library function

Changed files
+184 -1
doc
doc-support
lib
+1
doc/doc-support/default.nix
···
{ name = "lists"; description = "list manipulation functions"; }
{ name = "debug"; description = "debugging functions"; }
{ name = "options"; description = "NixOS / nixpkgs option handling"; }
+
{ name = "path"; description = "path functions"; }
{ name = "filesystem"; description = "filesystem functions"; }
{ name = "sources"; description = "source filtering functions"; }
{ name = "cli"; description = "command-line serialization functions"; }
+2 -1
lib/default.nix
···
maintainers = import ../maintainers/maintainer-list.nix;
teams = callLibs ../maintainers/team-list.nix;
meta = callLibs ./meta.nix;
-
sources = callLibs ./sources.nix;
versions = callLibs ./versions.nix;
# module system
···
fetchers = callLibs ./fetchers.nix;
# Eval-time filesystem handling
+
path = callLibs ./path;
filesystem = callLibs ./filesystem.nix;
+
sources = callLibs ./sources.nix;
# back-compat aliases
platforms = self.systems.doubles;
+75
lib/path/default.nix
···
+
# Functions for working with paths, see ./path.md
+
{ lib }:
+
let
+
+
inherit (builtins)
+
isString
+
match
+
;
+
+
inherit (lib.strings)
+
substring
+
;
+
+
inherit (lib.asserts)
+
assertMsg
+
;
+
+
# Return the reason why a subpath is invalid, or `null` if it's valid
+
subpathInvalidReason = value:
+
if ! isString value then
+
"The given value is of type ${builtins.typeOf value}, but a string was expected"
+
else if value == "" then
+
"The given string is empty"
+
else if substring 0 1 value == "/" then
+
"The given string \"${value}\" starts with a `/`, representing an absolute path"
+
# We don't support ".." components, see ./path.md#parent-directory
+
else if match "(.*/)?\\.\\.(/.*)?" value != null then
+
"The given string \"${value}\" contains a `..` component, which is not allowed in subpaths"
+
else null;
+
+
in /* No rec! Add dependencies on this file at the top. */ {
+
+
+
/* Whether a value is a valid subpath string.
+
+
- The value is a string
+
+
- The string is not empty
+
+
- The string doesn't start with a `/`
+
+
- The string doesn't contain any `..` path components
+
+
Type:
+
subpath.isValid :: String -> Bool
+
+
Example:
+
# Not a string
+
subpath.isValid null
+
=> false
+
+
# Empty string
+
subpath.isValid ""
+
=> false
+
+
# Absolute path
+
subpath.isValid "/foo"
+
=> false
+
+
# Contains a `..` path component
+
subpath.isValid "../foo"
+
=> false
+
+
# Valid subpath
+
subpath.isValid "foo/bar"
+
=> true
+
+
# Doesn't need to be normalised
+
subpath.isValid "./foo//bar/"
+
=> true
+
*/
+
subpath.isValid = value:
+
subpathInvalidReason value == null;
+
+
}
+27
lib/path/tests/default.nix
···
+
{
+
nixpkgs ? ../../..,
+
system ? builtins.currentSystem,
+
pkgs ? import nixpkgs {
+
config = {};
+
overlays = [];
+
inherit system;
+
},
+
libpath ? ../..,
+
}:
+
pkgs.runCommand "lib-path-tests" {
+
nativeBuildInputs = with pkgs; [
+
nix
+
];
+
} ''
+
# Needed to make Nix evaluation work
+
export NIX_STATE_DIR=$(mktemp -d)
+
+
cp -r ${libpath} lib
+
export TEST_LIB=$PWD/lib
+
+
echo "Running unit tests lib/path/tests/unit.nix"
+
nix-instantiate --eval lib/path/tests/unit.nix \
+
--argstr libpath "$TEST_LIB"
+
+
touch $out
+
''
+76
lib/path/tests/unit.nix
···
+
# Unit tests for lib.path functions. Use `nix-build` in this directory to
+
# run these
+
{ libpath }:
+
let
+
lib = import libpath;
+
inherit (lib.path) subpath;
+
+
cases = lib.runTests {
+
testSubpathIsValidExample1 = {
+
expr = subpath.isValid null;
+
expected = false;
+
};
+
testSubpathIsValidExample2 = {
+
expr = subpath.isValid "";
+
expected = false;
+
};
+
testSubpathIsValidExample3 = {
+
expr = subpath.isValid "/foo";
+
expected = false;
+
};
+
testSubpathIsValidExample4 = {
+
expr = subpath.isValid "../foo";
+
expected = false;
+
};
+
testSubpathIsValidExample5 = {
+
expr = subpath.isValid "foo/bar";
+
expected = true;
+
};
+
testSubpathIsValidExample6 = {
+
expr = subpath.isValid "./foo//bar/";
+
expected = true;
+
};
+
testSubpathIsValidTwoDotsEnd = {
+
expr = subpath.isValid "foo/..";
+
expected = false;
+
};
+
testSubpathIsValidTwoDotsMiddle = {
+
expr = subpath.isValid "foo/../bar";
+
expected = false;
+
};
+
testSubpathIsValidTwoDotsPrefix = {
+
expr = subpath.isValid "..foo";
+
expected = true;
+
};
+
testSubpathIsValidTwoDotsSuffix = {
+
expr = subpath.isValid "foo..";
+
expected = true;
+
};
+
testSubpathIsValidTwoDotsPrefixComponent = {
+
expr = subpath.isValid "foo/..bar/baz";
+
expected = true;
+
};
+
testSubpathIsValidTwoDotsSuffixComponent = {
+
expr = subpath.isValid "foo/bar../baz";
+
expected = true;
+
};
+
testSubpathIsValidThreeDots = {
+
expr = subpath.isValid "...";
+
expected = true;
+
};
+
testSubpathIsValidFourDots = {
+
expr = subpath.isValid "....";
+
expected = true;
+
};
+
testSubpathIsValidThreeDotsComponent = {
+
expr = subpath.isValid "foo/.../bar";
+
expected = true;
+
};
+
testSubpathIsValidFourDotsComponent = {
+
expr = subpath.isValid "foo/..../bar";
+
expected = true;
+
};
+
};
+
in
+
if cases == [] then "Unit tests successful"
+
else throw "Path unit tests failed: ${lib.generators.toPretty {} cases}"
+3
lib/tests/release.nix
···
inherit pkgs;
lib = import ../.;
})
+
(import ../path/tests {
+
inherit pkgs;
+
})
];
} ''
datadir="${pkgs.nix}/share"