Fix dir with ignored contents bug

This commit is contained in:
Robert Hensing 2023-09-07 16:58:51 +02:00
parent 44b3f2d6d2
commit b5594318e5
5 changed files with 94 additions and 8 deletions

View file

@ -2,6 +2,7 @@
let
parse-ini = import ./parse-git-config.nix { inherit lib; };
parse-gitignore = import ./rules.nix { inherit lib; };
path = import ./lib/path.nix { inherit lib; };
in
rec {
inherit (builtins) dirOf baseNameOf abort split hasAttr readFile readDir pathExists;
@ -27,14 +28,20 @@ rec {
lib.optional (extraRules != null) { contextDir = basePath; rules = extraRules; };
patternsBelowP = findPatternsTree extraRules2 basePath;
basePathStr = toString basePath;
in
path: type: let
localDirPath = removePrefix basePathStr (toString (dirOf path));
localDirPathElements = splitString "/" localDirPath;
patternResult = parse-gitignore.runFilterPattern (getPatterns patternsBelowP localDirPathElements)."/patterns" path type;
nonempty = any (nodeName: gitignoreFilter (basePath + "/${nodeName}") != false)
(attrNames (readDir path));
in patternResult && (type == "directory" -> nonempty);
rawFilter =
path: type: let
localDirPath = removePrefix basePathStr (toString (dirOf path));
localDirPathElements = splitString "/" localDirPath;
patternResult = parse-gitignore.runFilterPattern (getPatterns patternsBelowP localDirPathElements)."/patterns" path type;
contents = readDir path;
nonempty = localDirPathElements == [] || any (nodeName: memoFilter (path + "/${nodeName}") != false)
(attrNames contents);
in patternResult && (type == "directory" -> nonempty);
memoFilter =
path.memoize rawFilter
(p: throw "Could not find path ${toString p} in memo table. Did path or filtering semantics change?")
basePath;
in path: type: memoFilter path;
getPatterns =
patternTree: pathElems:

79
lib/path.nix Normal file
View file

@ -0,0 +1,79 @@
{ lib }:
let
inherit (lib) filter head mapAttrs tail;
# absolutePathComponentsBetween : PathOrString -> PathOrString -> [String]
# absolutePathComponentsBetween ancestor descendant
#
# Returns the path components that form the path from ancestor to descendant.
# Will not return ".." components, which is a feature. Throws when ancestor
# and descendant arguments aren't in said relation to each other.
#
# Example:
#
# absolutePathComponentsBetween /a /a/b/c == ["b" "c"]
# absolutePathComponentsBetween /a/b/c /a/b/c == []
absolutePathComponentsBetween =
ancestor: descendant:
let
a' = /. + ancestor;
go = d:
if a' == d
then []
else if d == /.
then throw "absolutePathComponentsBetween: path ${toString ancestor} is not an ancestor of ${toString descendant}"
else go (dirOf d) ++ [(baseNameOf d)];
in
go (/. + descendant);
/*
Memoize a function that takes a path argument.
Example:
analyzeTree = dir:
let g = memoizePathFunction (p: t: expensiveFunction p t) (p: {}) dir;
in presentExpensiveData g;
Type:
memoizePathFunction :: (Path -> Type -> a) -> (Path -> a) -> Path -> (Path -> a)
*/
memoize =
# Function to memoize
f:
# What to return when a path does not exist, as a function of the path
missing:
# Filesystem location below which the returned function is defined. `/.` may be acceptable, but a path closer to the data of interest is better.
root:
let
makeTree = dir: type: {
value = f dir type;
inherit type;
children =
if type == "directory"
then mapAttrs
(key: type: makeTree (dir + "/${key}") type)
(builtins.readDir dir)
else {};
};
# This is where the memoization happens
tree = makeTree root (
# We can't query the type of a store path in Nix without readFileType in
# pure mode, so we assume.
"directory"
);
lookup = notFound: list: subtree:
if list == []
then subtree.value
else if subtree.children ? ${head list}
then lookup notFound (tail list) subtree.children.${head list}
else notFound;
in
path: lookup
(missing path)
(absolutePathComponentsBetween root path)
tree;
in
{
inherit memoize absolutePathComponentsBetween;
}

0
tests/x.bar Normal file
View file

0
tests/x.foo Normal file
View file

0
tests/x.qux Normal file
View file