-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide more comprehensive exhaustiveness queries for Program
s and matches.
#53
Comments
This is somewhat of a tangent, but I have a few more thoughts I wanted to write down about this and the changes it could allow. In the examples from my comment above, With better support of exhaustiveness queries and more generally accepting The pub trait Program<'t>: ... {
fn is_match<'p>(&self, candidate: impl Into<CandidatePath<'p>>) -> bool;
fn matched<'p>(&self, candidate: &'p CandidatePath<'_>) -> Option<MatchedPath<'p>>;
// This probably shouldn't be a member of `Program`, but is included here for illustration.
fn residue<'p>(&self, candidate: &'p CandidatePath<'_>) -> Option<EntryResidue> {
self.matched(candidate).map(|matched| {
if matched.is_exhaustive() {
EntryResidue::Tree
}
else {
EntryResidue::File
}
})
}
fn exhaustiveness(&self) -> When;
}
impl<'p> MatchedPath<'p> {
fn text(&self) -> &MatchedText<'p> { ... }
fn is_exhaustive(&self) -> bool { ... }
} The pub trait FileIterator: ... {
type Entry: ...;
type Residue: ...;
fn not_pattern<'t>(self, program: impl Program<'t>) -> Not<'t, Self>; // All that is needed for nominal filters.
fn not_hidden(self) -> NotHidden<Self>; // A (sometimes) non-nominal filter provided for convenience.
} With this, the let glob = Glob::new(...).unwrap();
let except = wax::except(
wax::any([..., ...]).unwrap(), // Negation.
..., // Override. Allow this pattern despite the negation.
).unwrap();
for entry in glob.walk(".").not_pattern(except) { ... } Similarly, let glob = Glob::new(...).unwrap();
let except = gitignore::local_from_path(".").unwrap();
for entry in glob.walk(".").not_pattern(except) { ... } Something more like what the let path = Path::new(".");
let except = gitignore::local_from_path(path).unwrap();
for entry in path
.walk()
.not_hidden()
.not_pattern(except)
{
...
} I like the shape of these APIs. In essence, it pushes the complexity of composing patterns out of Lastly, something like this could be done today by always paying the cost of partitioning patterns in |
Exhaustiveness describes the extent of a match in a directory tree. A
Program
(glob) is exhaustive if a match indicates that any path in the sub-tree must also match the pattern. Today, this is provided in the public API byProgram::is_exhaustive
.Program::is_exhaustive
returns abool
, but this should really be a trivalent logic type. Some patterns have indeterminate exhaustiveness in the absence of a specific candidate path due to alternatives. For example,{**/*.txt,foo/**}
is indeterminate: it is nonexhaustive when matching the pathbar/baz.txt
but is exhaustive when matching the pathfoo
. I plan to introduce a more appropriate type to represent this like the following:Given a candidate path, it should be possible to definitively determine the exhaustiveness of a match, so a
bool
can be used in that context: the match is either exhaustive or not. This could be provided byProgram::matched
:Unfortunately, there are some big challenges in implementing this.
When::Sometimes
) and I'm not yet sure how to accomplish that.So why do this? 😄 Well, not only would it provide a more correct API (though I realize that the overwhelming majority of users don't care about these APIs), but it would also remove the need to partition patterns into exhaustive and nonexhaustive patterns in negations. In particular, the
FileIterator::not
combinator would no longer need to accept a sequence of patterns and could instead accept a single pattern. This could of course be anany
combinator, so it would still be possible to use multiple patterns in a singlenot
.This removes the need for
not
to handle building programs and emitting errors. More importantly, it also prevents poor performance caused by pathological inputs. Building both nonexhaustive and exhaustive patterns into anany
combinator can cause a negation to consider all matches nonexhaustive and therefore read directory trees when it may not be necessary. I also plan to introduce additional combinators that would also benefit from this for the same reasons. For example, anot_except
combinator:I prefer these APIs, but they cannot efficiently filter without knowing about exhaustiveness. Given only a single pattern, these APIs would rely on exhaustiveness information in
Program::matched
rather thanProgram::exhaustiveness
.The text was updated successfully, but these errors were encountered: