lib.types: introduce a fileset type (#428293)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
lib/tests/modules/fileset.nix
Normal file
50
lib/tests/modules/fileset.nix
Normal file
@@ -0,0 +1,50 @@
|
||||
{ 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
lib/tests/modules/fileset/keepme
Normal file
1
lib/tests/modules/fileset/keepme
Normal file
@@ -0,0 +1 @@
|
||||
Do not remove. This file is used by the tests in `../fileset.nix`.
|
||||
@@ -66,6 +66,11 @@ let
|
||||
fixupOptionType
|
||||
mergeOptionDecls
|
||||
;
|
||||
inherit (lib.fileset)
|
||||
isFileset
|
||||
unions
|
||||
empty
|
||||
;
|
||||
|
||||
inAttrPosSuffix =
|
||||
v: name:
|
||||
@@ -618,6 +623,15 @@ let
|
||||
};
|
||||
};
|
||||
|
||||
fileset = mkOptionType {
|
||||
name = "fileset";
|
||||
description = "fileset";
|
||||
descriptionClass = "noun";
|
||||
check = isFileset;
|
||||
merge = loc: defs: unions (map (x: x.value) defs);
|
||||
emptyValue.value = empty;
|
||||
};
|
||||
|
||||
# A package is a top-level store path (/nix/store/hash-name). This includes:
|
||||
# - derivations
|
||||
# - more generally, attribute sets with an `outPath` or `__toString` attribute
|
||||
|
||||
Reference in New Issue
Block a user