Patchwork D7914: rust-matchers: implement `visit_children_set` for `FileMatcher`

login
register
mail settings
Submitter phabricator
Date Feb. 11, 2020, 5:32 a.m.
Message ID <36b57b161460460208eeea77f6939324@localhost.localdomain>
Download mbox | patch
Permalink /patch/45164/
State Not Applicable
Headers show

Comments

phabricator - Feb. 11, 2020, 5:32 a.m.
Closed by commit rHG54d185eb24b5: rust-matchers: implement `visit_children_set` for `FileMatcher` (authored by Alphare).
This revision was automatically updated to reflect the committed changes.
This revision was not accepted when it landed; it landed in state "Needs Review".

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D7914?vs=19388&id=20146

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7914/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7914

AFFECTED FILES
  rust/hg-core/src/matchers.rs

CHANGE DETAILS




To: Alphare, #hg-reviewers
Cc: martinvonz, durin42, kevincox, mercurial-devel

Patch

diff --git a/rust/hg-core/src/matchers.rs b/rust/hg-core/src/matchers.rs
--- a/rust/hg-core/src/matchers.rs
+++ b/rust/hg-core/src/matchers.rs
@@ -10,7 +10,9 @@ 
 use crate::{utils::hg_path::HgPath, DirsMultiset, DirstateMapError};
 use std::collections::HashSet;
 use std::iter::FromIterator;
+use std::ops::Deref;
 
+#[derive(Debug, PartialEq)]
 pub enum VisitChildrenSet<'a> {
     /// Don't visit anything
     Empty,
@@ -163,12 +165,48 @@ 
     }
     fn visit_children_set(
         &self,
-        _directory: impl AsRef<HgPath>,
+        directory: impl AsRef<HgPath>,
     ) -> VisitChildrenSet {
-        // TODO implement once we have `status.traverse`
-        // This is useless until unknown files are taken into account
-        // Which will not need to happen before the `IncludeMatcher`.
-        unimplemented!()
+        if self.files.is_empty() || !self.dirs.contains(&directory) {
+            return VisitChildrenSet::Empty;
+        }
+        let dirs_as_set = self.dirs.iter().map(|k| k.deref()).collect();
+
+        let mut candidates: HashSet<&HgPath> =
+            self.files.union(&dirs_as_set).map(|k| *k).collect();
+        candidates.remove(HgPath::new(b""));
+
+        if !directory.as_ref().is_empty() {
+            let directory = [directory.as_ref().as_bytes(), b"/"].concat();
+            candidates = candidates
+                .iter()
+                .filter_map(|c| {
+                    if c.as_bytes().starts_with(&directory) {
+                        Some(HgPath::new(&c.as_bytes()[directory.len()..]))
+                    } else {
+                        None
+                    }
+                })
+                .collect();
+        }
+
+        // `self.dirs` includes all of the directories, recursively, so if
+        // we're attempting to match 'foo/bar/baz.txt', it'll have '', 'foo',
+        // 'foo/bar' in it. Thus we can safely ignore a candidate that has a
+        // '/' in it, indicating it's for a subdir-of-a-subdir; the immediate
+        // subdir will be in there without a slash.
+        VisitChildrenSet::Set(
+            candidates
+                .iter()
+                .filter_map(|c| {
+                    if c.bytes().all(|b| *b != b'/') {
+                        Some(*c)
+                    } else {
+                        None
+                    }
+                })
+                .collect(),
+        )
     }
     fn matches_everything(&self) -> bool {
         false
@@ -177,3 +215,107 @@ 
         true
     }
 }
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use pretty_assertions::assert_eq;
+
+    #[test]
+    fn test_filematcher_visit_children_set() {
+        // Visitchildrenset
+        let files = vec![HgPath::new(b"dir/subdir/foo.txt")];
+        let matcher = FileMatcher::new(&files).unwrap();
+
+        let mut set = HashSet::new();
+        set.insert(HgPath::new(b"dir"));
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"")),
+            VisitChildrenSet::Set(set)
+        );
+
+        let mut set = HashSet::new();
+        set.insert(HgPath::new(b"subdir"));
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"dir")),
+            VisitChildrenSet::Set(set)
+        );
+
+        let mut set = HashSet::new();
+        set.insert(HgPath::new(b"foo.txt"));
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"dir/subdir")),
+            VisitChildrenSet::Set(set)
+        );
+
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"dir/subdir/x")),
+            VisitChildrenSet::Empty
+        );
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"dir/subdir/foo.txt")),
+            VisitChildrenSet::Empty
+        );
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"folder")),
+            VisitChildrenSet::Empty
+        );
+    }
+
+    #[test]
+    fn test_filematcher_visit_children_set_files_and_dirs() {
+        let files = vec![
+            HgPath::new(b"rootfile.txt"),
+            HgPath::new(b"a/file1.txt"),
+            HgPath::new(b"a/b/file2.txt"),
+            // No file in a/b/c
+            HgPath::new(b"a/b/c/d/file4.txt"),
+        ];
+        let matcher = FileMatcher::new(&files).unwrap();
+
+        let mut set = HashSet::new();
+        set.insert(HgPath::new(b"a"));
+        set.insert(HgPath::new(b"rootfile.txt"));
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"")),
+            VisitChildrenSet::Set(set)
+        );
+
+        let mut set = HashSet::new();
+        set.insert(HgPath::new(b"b"));
+        set.insert(HgPath::new(b"file1.txt"));
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"a")),
+            VisitChildrenSet::Set(set)
+        );
+
+        let mut set = HashSet::new();
+        set.insert(HgPath::new(b"c"));
+        set.insert(HgPath::new(b"file2.txt"));
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"a/b")),
+            VisitChildrenSet::Set(set)
+        );
+
+        let mut set = HashSet::new();
+        set.insert(HgPath::new(b"d"));
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"a/b/c")),
+            VisitChildrenSet::Set(set)
+        );
+        let mut set = HashSet::new();
+        set.insert(HgPath::new(b"file4.txt"));
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"a/b/c/d")),
+            VisitChildrenSet::Set(set)
+        );
+
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"a/b/c/d/e")),
+            VisitChildrenSet::Empty
+        );
+        assert_eq!(
+            matcher.visit_children_set(HgPath::new(b"folder")),
+            VisitChildrenSet::Empty
+        );
+    }
+}