Mercurial > hg
changeset 49347:b508cffd3c04
rust: add UnionMatcher
This will be used in the upcoming support for sparse checkouts in
Rust-augmented status and later in rhg support for sparse checkouts.
author | Raphaël Gomès <rgomes@octobus.net> |
---|---|
date | Wed, 08 Jun 2022 11:55:40 +0200 |
parents | 75119bbee3d1 |
children | 0043c7aa3250 |
files | rust/hg-core/src/matchers.rs |
diffstat | 1 files changed, 222 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/rust/hg-core/src/matchers.rs Wed Jun 08 15:30:58 2022 +0200 +++ b/rust/hg-core/src/matchers.rs Wed Jun 08 11:55:40 2022 +0200 @@ -304,6 +304,70 @@ } } +/// The union of multiple matchers. Will match if any of the matchers match. +pub struct UnionMatcher { + matchers: Vec<Box<dyn Matcher + Sync>>, +} + +impl Matcher for UnionMatcher { + fn file_set(&self) -> Option<&HashSet<HgPathBuf>> { + None + } + + fn exact_match(&self, _filename: &HgPath) -> bool { + false + } + + fn matches(&self, filename: &HgPath) -> bool { + self.matchers.iter().any(|m| m.matches(filename)) + } + + fn visit_children_set(&self, directory: &HgPath) -> VisitChildrenSet { + let mut result = HashSet::new(); + let mut this = false; + for matcher in self.matchers.iter() { + let visit = matcher.visit_children_set(directory); + match visit { + VisitChildrenSet::Empty => continue, + VisitChildrenSet::This => { + this = true; + // Don't break, we might have an 'all' in here. + continue; + } + VisitChildrenSet::Set(set) => { + result.extend(set); + } + VisitChildrenSet::Recursive => { + return visit; + } + } + } + if this { + return VisitChildrenSet::This; + } + if result.is_empty() { + VisitChildrenSet::Empty + } else { + VisitChildrenSet::Set(result) + } + } + + fn matches_everything(&self) -> bool { + // TODO Maybe if all are AlwaysMatcher? + false + } + + fn is_exact(&self) -> bool { + false + } +} + +impl UnionMatcher { + pub fn new(matchers: Vec<Box<dyn Matcher + Sync>>) -> Self { + Self { matchers } + } +} + /// Returns a function that matches an `HgPath` against the given regex /// pattern. /// @@ -925,4 +989,162 @@ VisitChildrenSet::This ); } + + #[test] + fn test_unionmatcher() { + // Path + Rootfiles + let m1 = IncludeMatcher::new(vec![IgnorePattern::new( + PatternSyntax::RelPath, + b"dir/subdir", + Path::new(""), + )]) + .unwrap(); + let m2 = IncludeMatcher::new(vec![IgnorePattern::new( + PatternSyntax::RootFiles, + b"dir", + Path::new(""), + )]) + .unwrap(); + let matcher = UnionMatcher::new(vec![Box::new(m1), Box::new(m2)]); + + let mut set = HashSet::new(); + set.insert(HgPathBuf::from_bytes(b"dir")); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"")), + VisitChildrenSet::Set(set) + ); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir")), + VisitChildrenSet::This + ); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/subdir")), + VisitChildrenSet::Recursive + ); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/foo")), + VisitChildrenSet::Empty + ); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"folder")), + VisitChildrenSet::Empty + ); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"folder")), + VisitChildrenSet::Empty + ); + + // OPT: These next two could be 'all' instead of 'this'. + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), + VisitChildrenSet::This + ); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), + VisitChildrenSet::This + ); + + // Path + unrelated Path + let m1 = IncludeMatcher::new(vec![IgnorePattern::new( + PatternSyntax::RelPath, + b"dir/subdir", + Path::new(""), + )]) + .unwrap(); + let m2 = IncludeMatcher::new(vec![IgnorePattern::new( + PatternSyntax::RelPath, + b"folder", + Path::new(""), + )]) + .unwrap(); + let matcher = UnionMatcher::new(vec![Box::new(m1), Box::new(m2)]); + + let mut set = HashSet::new(); + set.insert(HgPathBuf::from_bytes(b"folder")); + set.insert(HgPathBuf::from_bytes(b"dir")); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"")), + VisitChildrenSet::Set(set) + ); + let mut set = HashSet::new(); + set.insert(HgPathBuf::from_bytes(b"subdir")); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir")), + VisitChildrenSet::Set(set) + ); + + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/subdir")), + VisitChildrenSet::Recursive + ); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/foo")), + VisitChildrenSet::Empty + ); + + assert_eq!( + matcher.visit_children_set(HgPath::new(b"folder")), + VisitChildrenSet::Recursive + ); + // OPT: These next two could be 'all' instead of 'this'. + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), + VisitChildrenSet::This + ); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), + VisitChildrenSet::This + ); + + // Path + subpath + let m1 = IncludeMatcher::new(vec![IgnorePattern::new( + PatternSyntax::RelPath, + b"dir/subdir/x", + Path::new(""), + )]) + .unwrap(); + let m2 = IncludeMatcher::new(vec![IgnorePattern::new( + PatternSyntax::RelPath, + b"dir/subdir", + Path::new(""), + )]) + .unwrap(); + let matcher = UnionMatcher::new(vec![Box::new(m1), Box::new(m2)]); + + let mut set = HashSet::new(); + set.insert(HgPathBuf::from_bytes(b"dir")); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"")), + VisitChildrenSet::Set(set) + ); + let mut set = HashSet::new(); + set.insert(HgPathBuf::from_bytes(b"subdir")); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir")), + VisitChildrenSet::Set(set) + ); + + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/subdir")), + VisitChildrenSet::Recursive + ); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/foo")), + VisitChildrenSet::Empty + ); + + assert_eq!( + matcher.visit_children_set(HgPath::new(b"folder")), + VisitChildrenSet::Empty + ); + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), + VisitChildrenSet::Recursive + ); + // OPT: this should probably be 'all' not 'this'. + assert_eq!( + matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), + VisitChildrenSet::This + ); + } }