Unverified Commit 49f0cbd7 authored by Robert Hensing's avatar Robert Hensing Committed by GitHub
Browse files

lib.types: introduce a `fileset` type (#428293)

parents c487ed86 ad1e615f
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ let

  inherit (import ./internal.nix { inherit lib; })
    _coerce
    _coerceResult
    _singleton
    _coerceMany
    _toSourceFilter
@@ -1005,4 +1006,49 @@ in
        {
          submodules = recurseSubmodules;
        };

  /**
    The empty fileset. It can be useful as a default value or as starting accumulator for a folding operation.

    # Type

    ```
    empty :: FileSet
    ```
  */
  empty = _emptyWithoutBase;

  /**
    Tests whether a given value is a fileset, or can be used in place of a fileset.

    # Inputs

    `value`

    : The value to test

    # Type

    ```
    isFileset :: Any -> Bool
    ```

    # Examples
    :::{.example}
    ## `lib.fileset.isFileset` usage example

    ```nix
    isFileset ./.
    => true

    isFileset (unions [  ])
    => true

    isFileset 1
    => false
    ```

    :::
  */
  isFileset = x: (_coerceResult "" x).success;
}
+35 −12
Original line number Diff line number Diff line
@@ -165,14 +165,27 @@ rec {
      _noEval = throw _noEvalMessage;
    };

  # Coerce a value to a fileset, erroring when the value cannot be coerced.
  # The string gives the context for error messages.
  # Type: String -> (fileset | Path) -> fileset
  _coerce =
  # Coerce a value to a fileset. Return a set containing the attribute `success`
  # indicating whether coercing succeeded, and either `value` when `success ==
  # true`, or an error `message` when `success == false`. The string gives the
  # context for error messages.
  #
  # Type: String -> (fileset | Path) -> { success :: Bool, value :: fileset } ] -> { success :: Bool, message :: String }
  _coerceResult =
    let
      ok = value: {
        success = true;
        inherit value;
      };
      error = message: {
        success = false;
        inherit message;
      };
    in
    context: value:
    if value._type or "" == "fileset" then
      if value._internalVersion > _currentVersion then
        throw ''
        error ''
          ${context} is a file set created from a future version of the file set library with a different internal representation:
              - Internal version of the file set: ${toString value._internalVersion}
              - Internal version of the library: ${toString _currentVersion}
@@ -184,27 +197,37 @@ rec {
            _currentVersion - value._internalVersion
          ) migrations;
        in
        foldl' (value: migration: migration value) value migrationsToApply
        ok (foldl' (value: migration: migration value) value migrationsToApply)
      else
        value
        ok value
    else if !isPath value then
      if value ? _isLibCleanSourceWith then
        throw ''
        error ''
          ${context} is a `lib.sources`-based value, but it should be a file set or a path instead.
              To convert a `lib.sources`-based value to a file set you can use `lib.fileset.fromSource`.
              Note that this only works for sources created from paths.''
      else if isStringLike value then
        throw ''
        error ''
          ${context} ("${toString value}") is a string-like value, but it should be a file set or a path instead.
              Paths represented as strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.''
      else
        throw ''${context} is of type ${typeOf value}, but it should be a file set or a path instead.''
        error ''${context} is of type ${typeOf value}, but it should be a file set or a path instead.''
    else if !pathExists value then
      throw ''
      error ''
        ${context} (${toString value}) is a path that does not exist.
            To create a file set from a path that may not exist, use `lib.fileset.maybeMissing`.''
    else
      _singleton value;
      ok (_singleton value);

  # Coerce a value to a fileset, erroring when the value cannot be coerced.
  # The string gives the context for error messages.
  # Type: String -> (fileset | Path) -> fileset
  _coerce =
    context: value:
    let
      result = _coerceResult context value;
    in
    if result.success then result.value else throw result.message;

  # Coerce many values to filesets, erroring when any value cannot be coerced,
  # or if the filesystem root of the values doesn't match.
+11 −0
Original line number Diff line number Diff line
@@ -223,6 +223,17 @@ checkConfigError 'A definition for option .* is not of type .path in the Nix sto
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store/.links"' config.pathInStore.bad4 ./types.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: "/foo/bar"' config.pathInStore.bad5 ./types.nix

# types.fileset
checkConfigOutput '^0$' config.filesetCardinal.ok1 ./fileset.nix
checkConfigOutput '^1$' config.filesetCardinal.ok2 ./fileset.nix
checkConfigOutput '^1$' config.filesetCardinal.ok3 ./fileset.nix
checkConfigOutput '^1$' config.filesetCardinal.ok4 ./fileset.nix
checkConfigOutput '^0$' config.filesetCardinal.ok5 ./fileset.nix
checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err1 ./fileset.nix
checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err2 ./fileset.nix
checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err3 ./fileset.nix
checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err4 ./fileset.nix

# Check boolean option.
checkConfigOutput '^false$' config.enable ./declare-enable.nix
checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./define-enable.nix
+50 −0
Original line number Diff line number Diff line
{ config, lib, ... }:

let
  inherit (lib)
    mkOption
    mkIf
    types
    mapAttrs
    length
    ;
  inherit (lib.fileset)
    empty
    unions
    toList
    ;
in

{
  options = {
    fileset = mkOption { type = with types; lazyAttrsOf fileset; };

    ## The following option is only here as a proxy to test `fileset` that does
    ## not work so well with `modules.sh` because it is not JSONable. It exposes
    ## the number of elements in the fileset.
    filesetCardinal = mkOption { default = mapAttrs (_: fs: length (toList fs)) config.fileset; };
  };

  config = {
    fileset.ok1 = empty;
    fileset.ok2 = ./fileset;
    fileset.ok3 = unions [
      empty
      ./fileset
    ];
    # fileset.ok4: see imports below
    fileset.ok5 = mkIf false ./fileset;

    fileset.err1 = 1;
    fileset.err2 = "foo";
    fileset.err3 = "./.";
    fileset.err4 = [ empty ];

  };

  imports = [
    { fileset.ok4 = ./fileset; }
    { fileset.ok4 = empty; }
    { fileset.ok4 = ./fileset; }
  ];
}
+1 −0
Original line number Diff line number Diff line
Do not remove. This file is used by the tests in `../fileset.nix`.
Loading