Merge pull request #260265 from tweag/fileset.fileFilter
`lib.fileset.fileFilter`: init
This commit is contained in:
commit
185acbce7d
3 changed files with 144 additions and 0 deletions
|
@ -6,6 +6,7 @@ let
|
||||||
_coerceMany
|
_coerceMany
|
||||||
_toSourceFilter
|
_toSourceFilter
|
||||||
_unionMany
|
_unionMany
|
||||||
|
_fileFilter
|
||||||
_printFileset
|
_printFileset
|
||||||
_intersection
|
_intersection
|
||||||
;
|
;
|
||||||
|
@ -41,6 +42,7 @@ let
|
||||||
;
|
;
|
||||||
|
|
||||||
inherit (lib.trivial)
|
inherit (lib.trivial)
|
||||||
|
isFunction
|
||||||
pipe
|
pipe
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -278,6 +280,55 @@ If a directory does not recursively contain any file, it is omitted from the sto
|
||||||
_unionMany
|
_unionMany
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
Filter a file set to only contain files matching some predicate.
|
||||||
|
|
||||||
|
Type:
|
||||||
|
fileFilter ::
|
||||||
|
({
|
||||||
|
name :: String,
|
||||||
|
type :: String,
|
||||||
|
...
|
||||||
|
} -> Bool)
|
||||||
|
-> FileSet
|
||||||
|
-> FileSet
|
||||||
|
|
||||||
|
Example:
|
||||||
|
# Include all regular `default.nix` files in the current directory
|
||||||
|
fileFilter (file: file.name == "default.nix") ./.
|
||||||
|
|
||||||
|
# Include all non-Nix files from the current directory
|
||||||
|
fileFilter (file: ! hasSuffix ".nix" file.name) ./.
|
||||||
|
|
||||||
|
# Include all files that start with a "." in the current directory
|
||||||
|
fileFilter (file: hasPrefix "." file.name) ./.
|
||||||
|
|
||||||
|
# Include all regular files (not symlinks or others) in the current directory
|
||||||
|
fileFilter (file: file.type == "regular")
|
||||||
|
*/
|
||||||
|
fileFilter =
|
||||||
|
/*
|
||||||
|
The predicate function to call on all files contained in given file set.
|
||||||
|
A file is included in the resulting file set if this function returns true for it.
|
||||||
|
|
||||||
|
This function is called with an attribute set containing these attributes:
|
||||||
|
|
||||||
|
- `name` (String): The name of the file
|
||||||
|
|
||||||
|
- `type` (String, one of `"regular"`, `"symlink"` or `"unknown"`): The type of the file.
|
||||||
|
This matches result of calling [`builtins.readFileType`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readFileType) on the file's path.
|
||||||
|
|
||||||
|
Other attributes may be added in the future.
|
||||||
|
*/
|
||||||
|
predicate:
|
||||||
|
# The file set to filter based on the predicate function
|
||||||
|
fileset:
|
||||||
|
if ! isFunction predicate then
|
||||||
|
throw "lib.fileset.fileFilter: Expected the first argument to be a function, but it's a ${typeOf predicate} instead."
|
||||||
|
else
|
||||||
|
_fileFilter predicate
|
||||||
|
(_coerce "lib.fileset.fileFilter: second argument" fileset);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The file set containing all files that are in both of two given file sets.
|
The file set containing all files that are in both of two given file sets.
|
||||||
See also [Intersection (set theory)](https://en.wikipedia.org/wiki/Intersection_(set_theory)).
|
See also [Intersection (set theory)](https://en.wikipedia.org/wiki/Intersection_(set_theory)).
|
||||||
|
|
|
@ -638,4 +638,30 @@ rec {
|
||||||
else
|
else
|
||||||
# In all other cases it's the rhs
|
# In all other cases it's the rhs
|
||||||
rhs;
|
rhs;
|
||||||
|
|
||||||
|
_fileFilter = predicate: fileset:
|
||||||
|
let
|
||||||
|
recurse = path: tree:
|
||||||
|
mapAttrs (name: subtree:
|
||||||
|
if isAttrs subtree || subtree == "directory" then
|
||||||
|
recurse (path + "/${name}") subtree
|
||||||
|
else if
|
||||||
|
predicate {
|
||||||
|
inherit name;
|
||||||
|
type = subtree;
|
||||||
|
# To ensure forwards compatibility with more arguments being added in the future,
|
||||||
|
# adding an attribute which can't be deconstructed :)
|
||||||
|
"lib.fileset.fileFilter: The predicate function passed as the first argument must be able to handle extra attributes for future compatibility. If you're using `{ name, file }:`, use `{ name, file, ... }:` instead." = null;
|
||||||
|
}
|
||||||
|
then
|
||||||
|
subtree
|
||||||
|
else
|
||||||
|
null
|
||||||
|
) (_directoryEntries path tree);
|
||||||
|
in
|
||||||
|
if fileset._internalIsEmptyWithoutBase then
|
||||||
|
_emptyWithoutBase
|
||||||
|
else
|
||||||
|
_create fileset._internalBase
|
||||||
|
(recurse fileset._internalBase fileset._internalTree);
|
||||||
}
|
}
|
||||||
|
|
|
@ -678,6 +678,73 @@ tree=(
|
||||||
checkFileset 'intersection (unions [ ./a/b ./c/d ./c/e ]) (unions [ ./a ./c/d/f ./c/e ])'
|
checkFileset 'intersection (unions [ ./a/b ./c/d ./c/e ]) (unions [ ./a ./c/d/f ./c/e ])'
|
||||||
|
|
||||||
|
|
||||||
|
## File filter
|
||||||
|
|
||||||
|
# The predicate is not called when there's no files
|
||||||
|
tree=()
|
||||||
|
checkFileset 'fileFilter (file: abort "this is not needed") ./.'
|
||||||
|
checkFileset 'fileFilter (file: abort "this is not needed") _emptyWithoutBase'
|
||||||
|
|
||||||
|
# The predicate must be able to handle extra attributes
|
||||||
|
touch a
|
||||||
|
expectFailure 'toSource { root = ./.; fileset = fileFilter ({ name, type }: true) ./.; }' 'called with unexpected argument '\''"lib.fileset.fileFilter: The predicate function passed as the first argument must be able to handle extra attributes for future compatibility. If you'\''re using `\{ name, file \}:`, use `\{ name, file, ... \}:` instead."'\'
|
||||||
|
rm -rf -- *
|
||||||
|
|
||||||
|
# .name is the name, and it works correctly, even recursively
|
||||||
|
tree=(
|
||||||
|
[a]=1
|
||||||
|
[b]=0
|
||||||
|
[c/a]=1
|
||||||
|
[c/b]=0
|
||||||
|
[d/c/a]=1
|
||||||
|
[d/c/b]=0
|
||||||
|
)
|
||||||
|
checkFileset 'fileFilter (file: file.name == "a") ./.'
|
||||||
|
tree=(
|
||||||
|
[a]=0
|
||||||
|
[b]=1
|
||||||
|
[c/a]=0
|
||||||
|
[c/b]=1
|
||||||
|
[d/c/a]=0
|
||||||
|
[d/c/b]=1
|
||||||
|
)
|
||||||
|
checkFileset 'fileFilter (file: file.name != "a") ./.'
|
||||||
|
|
||||||
|
# `.type` is the file type
|
||||||
|
mkdir d
|
||||||
|
touch d/a
|
||||||
|
ln -s d/b d/b
|
||||||
|
mkfifo d/c
|
||||||
|
expectEqual \
|
||||||
|
'toSource { root = ./.; fileset = fileFilter (file: file.type == "regular") ./.; }' \
|
||||||
|
'toSource { root = ./.; fileset = ./d/a; }'
|
||||||
|
expectEqual \
|
||||||
|
'toSource { root = ./.; fileset = fileFilter (file: file.type == "symlink") ./.; }' \
|
||||||
|
'toSource { root = ./.; fileset = ./d/b; }'
|
||||||
|
expectEqual \
|
||||||
|
'toSource { root = ./.; fileset = fileFilter (file: file.type == "unknown") ./.; }' \
|
||||||
|
'toSource { root = ./.; fileset = ./d/c; }'
|
||||||
|
expectEqual \
|
||||||
|
'toSource { root = ./.; fileset = fileFilter (file: file.type != "regular") ./.; }' \
|
||||||
|
'toSource { root = ./.; fileset = union ./d/b ./d/c; }'
|
||||||
|
expectEqual \
|
||||||
|
'toSource { root = ./.; fileset = fileFilter (file: file.type != "symlink") ./.; }' \
|
||||||
|
'toSource { root = ./.; fileset = union ./d/a ./d/c; }'
|
||||||
|
expectEqual \
|
||||||
|
'toSource { root = ./.; fileset = fileFilter (file: file.type != "unknown") ./.; }' \
|
||||||
|
'toSource { root = ./.; fileset = union ./d/a ./d/b; }'
|
||||||
|
rm -rf -- *
|
||||||
|
|
||||||
|
# It's lazy
|
||||||
|
tree=(
|
||||||
|
[b]=1
|
||||||
|
[c/a]=1
|
||||||
|
)
|
||||||
|
# Note that union evaluates the first argument first if necessary, that's why we can use ./c/a here
|
||||||
|
checkFileset 'union ./c/a (fileFilter (file: assert file.name != "a"; true) ./.)'
|
||||||
|
# but here we need to use ./c
|
||||||
|
checkFileset 'union (fileFilter (file: assert file.name != "a"; true) ./.) ./c'
|
||||||
|
|
||||||
## Tracing
|
## Tracing
|
||||||
|
|
||||||
# The second trace argument is returned
|
# The second trace argument is returned
|
||||||
|
|
Loading…
Reference in a new issue