From a99adc7c06a8eff52503f4376238b02ad92226a0 Mon Sep 17 00:00:00 2001 From: Niols Date: Thu, 10 Jul 2025 13:58:26 +0200 Subject: [PATCH] lib.fileset: add `isFileset` helper --- lib/fileset/default.nix | 35 ++++++++++++++++++++++++++++++ lib/fileset/internal.nix | 47 ++++++++++++++++++++++++++++++---------- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/lib/fileset/default.nix b/lib/fileset/default.nix index 3c51c6d4dab4..27a4d80f68ad 100644 --- a/lib/fileset/default.nix +++ b/lib/fileset/default.nix @@ -101,6 +101,7 @@ let inherit (import ./internal.nix { inherit lib; }) _coerce + _coerceResult _singleton _coerceMany _toSourceFilter @@ -1005,4 +1006,38 @@ in { submodules = recurseSubmodules; }; + + /** + 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; } diff --git a/lib/fileset/internal.nix b/lib/fileset/internal.nix index 59b8408ae8d6..342b284e1f71 100644 --- a/lib/fileset/internal.nix +++ b/lib/fileset/internal.nix @@ -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.