Unverified Commit a770c039 authored by Silvan Mosberger's avatar Silvan Mosberger Committed by GitHub
Browse files

Merge pull request #208887 from tweag/lib.path.append

lib.path.append: init
parents c2a12831 eac25387
Loading
Loading
Loading
Loading
+63 −7
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ let

  inherit (builtins)
    isString
    isPath
    split
    match
    ;
@@ -25,6 +26,10 @@ let
    assertMsg
    ;

  inherit (lib.path.subpath)
    isValid
    ;

  # Return the reason why a subpath is invalid, or `null` if it's valid
  subpathInvalidReason = value:
    if ! isString value then
@@ -94,6 +99,52 @@ let

in /* No rec! Add dependencies on this file at the top. */ {

  /* Append a subpath string to a path.

    Like `path + ("/" + string)` but safer, because it errors instead of returning potentially surprising results.
    More specifically, it checks that the first argument is a [path value type](https://nixos.org/manual/nix/stable/language/values.html#type-path"),
    and that the second argument is a valid subpath string (see `lib.path.subpath.isValid`).

    Type:
      append :: Path -> String -> Path

    Example:
      append /foo "bar/baz"
      => /foo/bar/baz

      # subpaths don't need to be normalised
      append /foo "./bar//baz/./"
      => /foo/bar/baz

      # can append to root directory
      append /. "foo/bar"
      => /foo/bar

      # first argument needs to be a path value type
      append "/foo" "bar"
      => <error>

      # second argument needs to be a valid subpath string
      append /foo /bar
      => <error>
      append /foo ""
      => <error>
      append /foo "/bar"
      => <error>
      append /foo "../bar"
      => <error>
  */
  append =
    # The absolute path to append to
    path:
    # The subpath string to append
    subpath:
    assert assertMsg (isPath path) ''
      lib.path.append: The first argument is of type ${builtins.typeOf path}, but a path was expected'';
    assert assertMsg (isValid subpath) ''
      lib.path.append: Second argument is not a valid subpath string:
          ${subpathInvalidReason subpath}'';
    path + ("/" + subpath);

  /* Whether a value is a valid subpath string.

@@ -133,7 +184,9 @@ in /* No rec! Add dependencies on this file at the top. */ {
    subpath.isValid "./foo//bar/"
    => true
  */
  subpath.isValid = value:
  subpath.isValid =
    # The value to check
    value:
    subpathInvalidReason value == null;


@@ -150,11 +203,11 @@ in /* No rec! Add dependencies on this file at the top. */ {

  Laws:

  - (Idempotency) Normalising multiple times gives the same result:
  - Idempotency - normalising multiple times gives the same result:

        subpath.normalise (subpath.normalise p) == subpath.normalise p

  - (Uniqueness) There's only a single normalisation for the paths that lead to the same file system node:
  - Uniqueness - there's only a single normalisation for the paths that lead to the same file system node:

        subpath.normalise p != subpath.normalise q -> $(realpath ${p}) != $(realpath ${q})

@@ -210,9 +263,12 @@ in /* No rec! Add dependencies on this file at the top. */ {
    subpath.normalise "/foo"
    => <error>
  */
  subpath.normalise = path:
    assert assertMsg (subpathInvalidReason path == null)
      "lib.path.subpath.normalise: Argument is not a valid subpath string: ${subpathInvalidReason path}";
    joinRelPath (splitRelPath path);
  subpath.normalise =
    # The subpath string to normalise
    subpath:
    assert assertMsg (isValid subpath) ''
      lib.path.subpath.normalise: Argument is not a valid subpath string:
          ${subpathInvalidReason subpath}'';
    joinRelPath (splitRelPath subpath);

}
+39 −1
Original line number Diff line number Diff line
@@ -3,9 +3,44 @@
{ libpath }:
let
  lib = import libpath;
  inherit (lib.path) subpath;
  inherit (lib.path) append subpath;

  cases = lib.runTests {
    # Test examples from the lib.path.append documentation
    testAppendExample1 = {
      expr = append /foo "bar/baz";
      expected = /foo/bar/baz;
    };
    testAppendExample2 = {
      expr = append /foo "./bar//baz/./";
      expected = /foo/bar/baz;
    };
    testAppendExample3 = {
      expr = append /. "foo/bar";
      expected = /foo/bar;
    };
    testAppendExample4 = {
      expr = (builtins.tryEval (append "/foo" "bar")).success;
      expected = false;
    };
    testAppendExample5 = {
      expr = (builtins.tryEval (append /foo /bar)).success;
      expected = false;
    };
    testAppendExample6 = {
      expr = (builtins.tryEval (append /foo "")).success;
      expected = false;
    };
    testAppendExample7 = {
      expr = (builtins.tryEval (append /foo "/bar")).success;
      expected = false;
    };
    testAppendExample8 = {
      expr = (builtins.tryEval (append /foo "../bar")).success;
      expected = false;
    };

    # Test examples from the lib.path.subpath.isValid documentation
    testSubpathIsValidExample1 = {
      expr = subpath.isValid null;
      expected = false;
@@ -30,6 +65,7 @@ let
      expr = subpath.isValid "./foo//bar/";
      expected = true;
    };
    # Some extra tests
    testSubpathIsValidTwoDotsEnd = {
      expr = subpath.isValid "foo/..";
      expected = false;
@@ -71,6 +107,7 @@ let
      expected = true;
    };

    # Test examples from the lib.path.subpath.normalise documentation
    testSubpathNormaliseExample1 = {
      expr = subpath.normalise "foo//bar";
      expected = "./foo/bar";
@@ -107,6 +144,7 @@ let
      expr = (builtins.tryEval (subpath.normalise "/foo")).success;
      expected = false;
    };
    # Some extra tests
    testSubpathNormaliseIsValidDots = {
      expr = subpath.normalise "./foo/.bar/.../baz...qux";
      expected = "./foo/.bar/.../baz...qux";