ok/jj
1
0
Fork 0
forked from mirrors/jj

matchers: let matchers indicate that directory matches recursively (#52)

Knowing that a matchers matches everything recursively from a certain
directory is useful for various optimizations. For example, it lets
you avoid visiting a directory if you're using a matcher with a
negative condition (so you return what does *not* match).
This commit is contained in:
Martin von Zweigbergk 2022-02-13 20:05:17 -08:00 committed by Martin von Zweigbergk
parent 38455503fc
commit 277b9bb08e

View file

@ -19,18 +19,14 @@ use std::collections::{BTreeSet, HashMap, HashSet};
use crate::repo_path::{RepoPath, RepoPathComponent}; use crate::repo_path::{RepoPath, RepoPathComponent};
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub struct Visit { pub enum Visit {
/// Everything in the directory is *guaranteed* to match, no need to check
/// descendants
AllRecursively,
Specific {
dirs: VisitDirs, dirs: VisitDirs,
files: VisitFiles, files: VisitFiles,
} },
impl Visit {
pub fn all() -> Self {
Self {
dirs: VisitDirs::All,
files: VisitFiles::All,
}
}
} }
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
@ -59,10 +55,7 @@ impl Matcher for EverythingMatcher {
} }
fn visit(&self, _dir: &RepoPath) -> Visit { fn visit(&self, _dir: &RepoPath) -> Visit {
Visit { Visit::AllRecursively
dirs: VisitDirs::All,
files: VisitFiles::All,
}
} }
} }
@ -90,7 +83,7 @@ impl Matcher for FilesMatcher {
fn visit(&self, dir: &RepoPath) -> Visit { fn visit(&self, dir: &RepoPath) -> Visit {
let dirs = self.dirs.get_dirs(dir); let dirs = self.dirs.get_dirs(dir);
let files = self.dirs.get_files(dir); let files = self.dirs.get_files(dir);
Visit { Visit::Specific {
dirs: VisitDirs::Set(dirs), dirs: VisitDirs::Set(dirs),
files: VisitFiles::Set(files), files: VisitFiles::Set(files),
} }
@ -131,11 +124,11 @@ impl Matcher for PrefixMatcher {
fn visit(&self, dir: &RepoPath) -> Visit { fn visit(&self, dir: &RepoPath) -> Visit {
if self.matches(dir) { if self.matches(dir) {
Visit::all() Visit::AllRecursively
} else { } else {
let dirs = self.dirs.get_dirs(dir); let dirs = self.dirs.get_dirs(dir);
let files = self.dirs.get_files(dir); let files = self.dirs.get_files(dir);
Visit { Visit::Specific {
dirs: VisitDirs::Set(dirs), dirs: VisitDirs::Set(dirs),
files: VisitFiles::Set(files), files: VisitFiles::Set(files),
} }
@ -244,7 +237,7 @@ mod tests {
assert!(!m.matches(&RepoPath::from_internal_string("dir/file"))); assert!(!m.matches(&RepoPath::from_internal_string("dir/file")));
assert_eq!( assert_eq!(
m.visit(&RepoPath::root()), m.visit(&RepoPath::root()),
Visit { Visit::Specific {
dirs: VisitDirs::Set(hashset! {}), dirs: VisitDirs::Set(hashset! {}),
files: VisitFiles::Set(hashset! {}), files: VisitFiles::Set(hashset! {}),
} }
@ -262,14 +255,14 @@ mod tests {
assert_eq!( assert_eq!(
m.visit(&RepoPath::root()), m.visit(&RepoPath::root()),
Visit { Visit::Specific {
dirs: VisitDirs::Set(hashset! {RepoPathComponent::from("dir1")}), dirs: VisitDirs::Set(hashset! {RepoPathComponent::from("dir1")}),
files: VisitFiles::Set(hashset! {RepoPathComponent::from("file4")}), files: VisitFiles::Set(hashset! {RepoPathComponent::from("file4")}),
} }
); );
assert_eq!( assert_eq!(
m.visit(&RepoPath::from_internal_string("dir1")), m.visit(&RepoPath::from_internal_string("dir1")),
Visit { Visit::Specific {
dirs: VisitDirs::Set( dirs: VisitDirs::Set(
hashset! {RepoPathComponent::from("subdir1"), RepoPathComponent::from("subdir2")} hashset! {RepoPathComponent::from("subdir1"), RepoPathComponent::from("subdir2")}
), ),
@ -278,7 +271,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
m.visit(&RepoPath::from_internal_string("dir1/subdir1")), m.visit(&RepoPath::from_internal_string("dir1/subdir1")),
Visit { Visit::Specific {
dirs: VisitDirs::Set(hashset! {}), dirs: VisitDirs::Set(hashset! {}),
files: VisitFiles::Set( files: VisitFiles::Set(
hashset! {RepoPathComponent::from("file1"), RepoPathComponent::from("file2")} hashset! {RepoPathComponent::from("file1"), RepoPathComponent::from("file2")}
@ -287,7 +280,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
m.visit(&RepoPath::from_internal_string("dir1/subdir2")), m.visit(&RepoPath::from_internal_string("dir1/subdir2")),
Visit { Visit::Specific {
dirs: VisitDirs::Set(hashset! {}), dirs: VisitDirs::Set(hashset! {}),
files: VisitFiles::Set(hashset! {RepoPathComponent::from("file3")}), files: VisitFiles::Set(hashset! {RepoPathComponent::from("file3")}),
} }
@ -301,7 +294,7 @@ mod tests {
assert!(!m.matches(&RepoPath::from_internal_string("dir/file"))); assert!(!m.matches(&RepoPath::from_internal_string("dir/file")));
assert_eq!( assert_eq!(
m.visit(&RepoPath::root()), m.visit(&RepoPath::root()),
Visit { Visit::Specific {
dirs: VisitDirs::Set(hashset! {}), dirs: VisitDirs::Set(hashset! {}),
files: VisitFiles::Set(hashset! {}), files: VisitFiles::Set(hashset! {}),
} }
@ -315,19 +308,10 @@ mod tests {
assert!(m.matches(&RepoPath::from_internal_string("file"))); assert!(m.matches(&RepoPath::from_internal_string("file")));
assert!(m.matches(&RepoPath::from_internal_string("dir/file"))); assert!(m.matches(&RepoPath::from_internal_string("dir/file")));
// Visits all directories // Visits all directories
assert_eq!( assert_eq!(m.visit(&RepoPath::root()), Visit::AllRecursively);
m.visit(&RepoPath::root()),
Visit {
dirs: VisitDirs::All,
files: VisitFiles::All,
}
);
assert_eq!( assert_eq!(
m.visit(&RepoPath::from_internal_string("foo/bar")), m.visit(&RepoPath::from_internal_string("foo/bar")),
Visit { Visit::AllRecursively
dirs: VisitDirs::All,
files: VisitFiles::All,
}
); );
} }
@ -352,7 +336,7 @@ mod tests {
// shouldn't be visited) // shouldn't be visited)
assert_eq!( assert_eq!(
m.visit(&RepoPath::root()), m.visit(&RepoPath::root()),
Visit { Visit::Specific {
dirs: VisitDirs::Set(hashset! {RepoPathComponent::from("foo")}), dirs: VisitDirs::Set(hashset! {RepoPathComponent::from("foo")}),
files: VisitFiles::Set(hashset! {}), files: VisitFiles::Set(hashset! {}),
} }
@ -361,33 +345,26 @@ mod tests {
// match // match
assert_eq!( assert_eq!(
m.visit(&RepoPath::from_internal_string("foo")), m.visit(&RepoPath::from_internal_string("foo")),
Visit { Visit::Specific {
dirs: VisitDirs::Set(hashset! {RepoPathComponent::from("bar")}), dirs: VisitDirs::Set(hashset! {RepoPathComponent::from("bar")}),
files: VisitFiles::Set(hashset! {RepoPathComponent::from("bar")}), files: VisitFiles::Set(hashset! {RepoPathComponent::from("bar")}),
} }
); );
// Inside a directory that matches the prefix, everything may match (in does in // Inside a directory that matches the prefix, everything matches recursively
// fact match, as tested by m.matches() earlier)
assert_eq!( assert_eq!(
m.visit(&RepoPath::from_internal_string("foo/bar")), m.visit(&RepoPath::from_internal_string("foo/bar")),
Visit { Visit::AllRecursively
dirs: VisitDirs::All,
files: VisitFiles::All,
}
); );
// Same thing in subdirectories of the prefix // Same thing in subdirectories of the prefix
assert_eq!( assert_eq!(
m.visit(&RepoPath::from_internal_string("foo/bar/baz")), m.visit(&RepoPath::from_internal_string("foo/bar/baz")),
Visit { Visit::AllRecursively
dirs: VisitDirs::All,
files: VisitFiles::All,
}
); );
// Nothing in directories that are siblings of the prefix can match, so don't // Nothing in directories that are siblings of the prefix can match, so don't
// visit // visit
assert_eq!( assert_eq!(
m.visit(&RepoPath::from_internal_string("bar")), m.visit(&RepoPath::from_internal_string("bar")),
Visit { Visit::Specific {
dirs: VisitDirs::Set(hashset! {}), dirs: VisitDirs::Set(hashset! {}),
files: VisitFiles::Set(hashset! {}), files: VisitFiles::Set(hashset! {}),
} }
@ -409,27 +386,20 @@ mod tests {
assert_eq!( assert_eq!(
m.visit(&RepoPath::root()), m.visit(&RepoPath::root()),
Visit { Visit::Specific {
dirs: VisitDirs::Set(hashset! {RepoPathComponent::from("foo")}), dirs: VisitDirs::Set(hashset! {RepoPathComponent::from("foo")}),
files: VisitFiles::Set(hashset! {RepoPathComponent::from("foo")}), files: VisitFiles::Set(hashset! {RepoPathComponent::from("foo")}),
} }
); );
// Inside a directory that matches the prefix, everything may match (in does in // Inside a directory that matches the prefix, everything matches recursively
// fact match, as tested by m.matches() earlier)
assert_eq!( assert_eq!(
m.visit(&RepoPath::from_internal_string("foo")), m.visit(&RepoPath::from_internal_string("foo")),
Visit { Visit::AllRecursively
dirs: VisitDirs::All,
files: VisitFiles::All,
}
); );
// Same thing in subdirectories of the prefix // Same thing in subdirectories of the prefix
assert_eq!( assert_eq!(
m.visit(&RepoPath::from_internal_string("foo/bar/baz")), m.visit(&RepoPath::from_internal_string("foo/bar/baz")),
Visit { Visit::AllRecursively
dirs: VisitDirs::All,
files: VisitFiles::All,
}
); );
} }
} }