rust/hg-core/src/matchers.rs
changeset 44353 54d185eb24b5
parent 44006 72bced4f2936
child 44519 52d40f8fb82d
equal deleted inserted replaced
44352:0bf3b5e80d30 44353:54d185eb24b5
     8 //! Structs and types for matching files and directories.
     8 //! Structs and types for matching files and directories.
     9 
     9 
    10 use crate::{utils::hg_path::HgPath, DirsMultiset, DirstateMapError};
    10 use crate::{utils::hg_path::HgPath, DirsMultiset, DirstateMapError};
    11 use std::collections::HashSet;
    11 use std::collections::HashSet;
    12 use std::iter::FromIterator;
    12 use std::iter::FromIterator;
    13 
    13 use std::ops::Deref;
       
    14 
       
    15 #[derive(Debug, PartialEq)]
    14 pub enum VisitChildrenSet<'a> {
    16 pub enum VisitChildrenSet<'a> {
    15     /// Don't visit anything
    17     /// Don't visit anything
    16     Empty,
    18     Empty,
    17     /// Only visit this directory
    19     /// Only visit this directory
    18     This,
    20     This,
   161     fn matches(&self, filename: impl AsRef<HgPath>) -> bool {
   163     fn matches(&self, filename: impl AsRef<HgPath>) -> bool {
   162         self.inner_matches(filename)
   164         self.inner_matches(filename)
   163     }
   165     }
   164     fn visit_children_set(
   166     fn visit_children_set(
   165         &self,
   167         &self,
   166         _directory: impl AsRef<HgPath>,
   168         directory: impl AsRef<HgPath>,
   167     ) -> VisitChildrenSet {
   169     ) -> VisitChildrenSet {
   168         // TODO implement once we have `status.traverse`
   170         if self.files.is_empty() || !self.dirs.contains(&directory) {
   169         // This is useless until unknown files are taken into account
   171             return VisitChildrenSet::Empty;
   170         // Which will not need to happen before the `IncludeMatcher`.
   172         }
   171         unimplemented!()
   173         let dirs_as_set = self.dirs.iter().map(|k| k.deref()).collect();
       
   174 
       
   175         let mut candidates: HashSet<&HgPath> =
       
   176             self.files.union(&dirs_as_set).map(|k| *k).collect();
       
   177         candidates.remove(HgPath::new(b""));
       
   178 
       
   179         if !directory.as_ref().is_empty() {
       
   180             let directory = [directory.as_ref().as_bytes(), b"/"].concat();
       
   181             candidates = candidates
       
   182                 .iter()
       
   183                 .filter_map(|c| {
       
   184                     if c.as_bytes().starts_with(&directory) {
       
   185                         Some(HgPath::new(&c.as_bytes()[directory.len()..]))
       
   186                     } else {
       
   187                         None
       
   188                     }
       
   189                 })
       
   190                 .collect();
       
   191         }
       
   192 
       
   193         // `self.dirs` includes all of the directories, recursively, so if
       
   194         // we're attempting to match 'foo/bar/baz.txt', it'll have '', 'foo',
       
   195         // 'foo/bar' in it. Thus we can safely ignore a candidate that has a
       
   196         // '/' in it, indicating it's for a subdir-of-a-subdir; the immediate
       
   197         // subdir will be in there without a slash.
       
   198         VisitChildrenSet::Set(
       
   199             candidates
       
   200                 .iter()
       
   201                 .filter_map(|c| {
       
   202                     if c.bytes().all(|b| *b != b'/') {
       
   203                         Some(*c)
       
   204                     } else {
       
   205                         None
       
   206                     }
       
   207                 })
       
   208                 .collect(),
       
   209         )
   172     }
   210     }
   173     fn matches_everything(&self) -> bool {
   211     fn matches_everything(&self) -> bool {
   174         false
   212         false
   175     }
   213     }
   176     fn is_exact(&self) -> bool {
   214     fn is_exact(&self) -> bool {
   177         true
   215         true
   178     }
   216     }
   179 }
   217 }
       
   218 #[cfg(test)]
       
   219 mod tests {
       
   220     use super::*;
       
   221     use pretty_assertions::assert_eq;
       
   222 
       
   223     #[test]
       
   224     fn test_filematcher_visit_children_set() {
       
   225         // Visitchildrenset
       
   226         let files = vec![HgPath::new(b"dir/subdir/foo.txt")];
       
   227         let matcher = FileMatcher::new(&files).unwrap();
       
   228 
       
   229         let mut set = HashSet::new();
       
   230         set.insert(HgPath::new(b"dir"));
       
   231         assert_eq!(
       
   232             matcher.visit_children_set(HgPath::new(b"")),
       
   233             VisitChildrenSet::Set(set)
       
   234         );
       
   235 
       
   236         let mut set = HashSet::new();
       
   237         set.insert(HgPath::new(b"subdir"));
       
   238         assert_eq!(
       
   239             matcher.visit_children_set(HgPath::new(b"dir")),
       
   240             VisitChildrenSet::Set(set)
       
   241         );
       
   242 
       
   243         let mut set = HashSet::new();
       
   244         set.insert(HgPath::new(b"foo.txt"));
       
   245         assert_eq!(
       
   246             matcher.visit_children_set(HgPath::new(b"dir/subdir")),
       
   247             VisitChildrenSet::Set(set)
       
   248         );
       
   249 
       
   250         assert_eq!(
       
   251             matcher.visit_children_set(HgPath::new(b"dir/subdir/x")),
       
   252             VisitChildrenSet::Empty
       
   253         );
       
   254         assert_eq!(
       
   255             matcher.visit_children_set(HgPath::new(b"dir/subdir/foo.txt")),
       
   256             VisitChildrenSet::Empty
       
   257         );
       
   258         assert_eq!(
       
   259             matcher.visit_children_set(HgPath::new(b"folder")),
       
   260             VisitChildrenSet::Empty
       
   261         );
       
   262     }
       
   263 
       
   264     #[test]
       
   265     fn test_filematcher_visit_children_set_files_and_dirs() {
       
   266         let files = vec![
       
   267             HgPath::new(b"rootfile.txt"),
       
   268             HgPath::new(b"a/file1.txt"),
       
   269             HgPath::new(b"a/b/file2.txt"),
       
   270             // No file in a/b/c
       
   271             HgPath::new(b"a/b/c/d/file4.txt"),
       
   272         ];
       
   273         let matcher = FileMatcher::new(&files).unwrap();
       
   274 
       
   275         let mut set = HashSet::new();
       
   276         set.insert(HgPath::new(b"a"));
       
   277         set.insert(HgPath::new(b"rootfile.txt"));
       
   278         assert_eq!(
       
   279             matcher.visit_children_set(HgPath::new(b"")),
       
   280             VisitChildrenSet::Set(set)
       
   281         );
       
   282 
       
   283         let mut set = HashSet::new();
       
   284         set.insert(HgPath::new(b"b"));
       
   285         set.insert(HgPath::new(b"file1.txt"));
       
   286         assert_eq!(
       
   287             matcher.visit_children_set(HgPath::new(b"a")),
       
   288             VisitChildrenSet::Set(set)
       
   289         );
       
   290 
       
   291         let mut set = HashSet::new();
       
   292         set.insert(HgPath::new(b"c"));
       
   293         set.insert(HgPath::new(b"file2.txt"));
       
   294         assert_eq!(
       
   295             matcher.visit_children_set(HgPath::new(b"a/b")),
       
   296             VisitChildrenSet::Set(set)
       
   297         );
       
   298 
       
   299         let mut set = HashSet::new();
       
   300         set.insert(HgPath::new(b"d"));
       
   301         assert_eq!(
       
   302             matcher.visit_children_set(HgPath::new(b"a/b/c")),
       
   303             VisitChildrenSet::Set(set)
       
   304         );
       
   305         let mut set = HashSet::new();
       
   306         set.insert(HgPath::new(b"file4.txt"));
       
   307         assert_eq!(
       
   308             matcher.visit_children_set(HgPath::new(b"a/b/c/d")),
       
   309             VisitChildrenSet::Set(set)
       
   310         );
       
   311 
       
   312         assert_eq!(
       
   313             matcher.visit_children_set(HgPath::new(b"a/b/c/d/e")),
       
   314             VisitChildrenSet::Empty
       
   315         );
       
   316         assert_eq!(
       
   317             matcher.visit_children_set(HgPath::new(b"folder")),
       
   318             VisitChildrenSet::Empty
       
   319         );
       
   320     }
       
   321 }