rust/hg-core/src/matchers.rs
changeset 45607 75f785888a7b
parent 44973 26114bd6ec60
child 47378 777c3d231913
equal deleted inserted replaced
45606:2a169781556e 45607:75f785888a7b
    47 
    47 
    48 pub trait Matcher {
    48 pub trait Matcher {
    49     /// Explicitly listed files
    49     /// Explicitly listed files
    50     fn file_set(&self) -> Option<&HashSet<&HgPath>>;
    50     fn file_set(&self) -> Option<&HashSet<&HgPath>>;
    51     /// Returns whether `filename` is in `file_set`
    51     /// Returns whether `filename` is in `file_set`
    52     fn exact_match(&self, filename: impl AsRef<HgPath>) -> bool;
    52     fn exact_match(&self, filename: &HgPath) -> bool;
    53     /// Returns whether `filename` is matched by this matcher
    53     /// Returns whether `filename` is matched by this matcher
    54     fn matches(&self, filename: impl AsRef<HgPath>) -> bool;
    54     fn matches(&self, filename: &HgPath) -> bool;
    55     /// Decides whether a directory should be visited based on whether it
    55     /// Decides whether a directory should be visited based on whether it
    56     /// has potential matches in it or one of its subdirectories, and
    56     /// has potential matches in it or one of its subdirectories, and
    57     /// potentially lists which subdirectories of that directory should be
    57     /// potentially lists which subdirectories of that directory should be
    58     /// visited. This is based on the match's primary, included, and excluded
    58     /// visited. This is based on the match's primary, included, and excluded
    59     /// patterns.
    59     /// patterns.
    87     /// it may return `VisitChildrenSet::This`.
    87     /// it may return `VisitChildrenSet::This`.
    88     /// Do not rely on the return being a `HashSet` indicating that there are
    88     /// Do not rely on the return being a `HashSet` indicating that there are
    89     /// no files in this dir to investigate (or equivalently that if there are
    89     /// no files in this dir to investigate (or equivalently that if there are
    90     /// files to investigate in 'dir' that it will always return
    90     /// files to investigate in 'dir' that it will always return
    91     /// `VisitChildrenSet::This`).
    91     /// `VisitChildrenSet::This`).
    92     fn visit_children_set(
    92     fn visit_children_set(&self, directory: &HgPath) -> VisitChildrenSet;
    93         &self,
       
    94         directory: impl AsRef<HgPath>,
       
    95     ) -> VisitChildrenSet;
       
    96     /// Matcher will match everything and `files_set()` will be empty:
    93     /// Matcher will match everything and `files_set()` will be empty:
    97     /// optimization might be possible.
    94     /// optimization might be possible.
    98     fn matches_everything(&self) -> bool;
    95     fn matches_everything(&self) -> bool;
    99     /// Matcher will match exactly the files in `files_set()`: optimization
    96     /// Matcher will match exactly the files in `files_set()`: optimization
   100     /// might be possible.
    97     /// might be possible.
   117 
   114 
   118 impl Matcher for AlwaysMatcher {
   115 impl Matcher for AlwaysMatcher {
   119     fn file_set(&self) -> Option<&HashSet<&HgPath>> {
   116     fn file_set(&self) -> Option<&HashSet<&HgPath>> {
   120         None
   117         None
   121     }
   118     }
   122     fn exact_match(&self, _filename: impl AsRef<HgPath>) -> bool {
   119     fn exact_match(&self, _filename: &HgPath) -> bool {
   123         false
   120         false
   124     }
   121     }
   125     fn matches(&self, _filename: impl AsRef<HgPath>) -> bool {
   122     fn matches(&self, _filename: &HgPath) -> bool {
   126         true
   123         true
   127     }
   124     }
   128     fn visit_children_set(
   125     fn visit_children_set(&self, _directory: &HgPath) -> VisitChildrenSet {
   129         &self,
       
   130         _directory: impl AsRef<HgPath>,
       
   131     ) -> VisitChildrenSet {
       
   132         VisitChildrenSet::Recursive
   126         VisitChildrenSet::Recursive
   133     }
   127     }
   134     fn matches_everything(&self) -> bool {
   128     fn matches_everything(&self) -> bool {
   135         true
   129         true
   136     }
   130     }
   141 
   135 
   142 /// Matches the input files exactly. They are interpreted as paths, not
   136 /// Matches the input files exactly. They are interpreted as paths, not
   143 /// patterns.
   137 /// patterns.
   144 ///
   138 ///
   145 ///```
   139 ///```
   146 /// use hg::{ matchers::{Matcher, FileMatcher}, utils::hg_path::HgPath };
   140 /// use hg::{ matchers::{Matcher, FileMatcher}, utils::hg_path::{HgPath, HgPathBuf} };
   147 ///
   141 ///
   148 /// let files = [HgPath::new(b"a.txt"), HgPath::new(br"re:.*\.c$")];
   142 /// let files = [HgPathBuf::from_bytes(b"a.txt"), HgPathBuf::from_bytes(br"re:.*\.c$")];
   149 /// let matcher = FileMatcher::new(&files).unwrap();
   143 /// let matcher = FileMatcher::new(&files).unwrap();
   150 ///
   144 ///
   151 /// assert_eq!(matcher.matches(HgPath::new(b"a.txt")), true);
   145 /// assert_eq!(matcher.matches(HgPath::new(b"a.txt")), true);
   152 /// assert_eq!(matcher.matches(HgPath::new(b"b.txt")), false);
   146 /// assert_eq!(matcher.matches(HgPath::new(b"b.txt")), false);
   153 /// assert_eq!(matcher.matches(HgPath::new(b"main.c")), false);
   147 /// assert_eq!(matcher.matches(HgPath::new(b"main.c")), false);
   158     files: HashSet<&'a HgPath>,
   152     files: HashSet<&'a HgPath>,
   159     dirs: DirsMultiset,
   153     dirs: DirsMultiset,
   160 }
   154 }
   161 
   155 
   162 impl<'a> FileMatcher<'a> {
   156 impl<'a> FileMatcher<'a> {
   163     pub fn new(
   157     pub fn new(files: &'a [HgPathBuf]) -> Result<Self, DirstateMapError> {
   164         files: &'a [impl AsRef<HgPath>],
       
   165     ) -> Result<Self, DirstateMapError> {
       
   166         Ok(Self {
   158         Ok(Self {
   167             files: HashSet::from_iter(files.iter().map(AsRef::as_ref)),
   159             files: HashSet::from_iter(files.iter().map(AsRef::as_ref)),
   168             dirs: DirsMultiset::from_manifest(files)?,
   160             dirs: DirsMultiset::from_manifest(files)?,
   169         })
   161         })
   170     }
   162     }
   171     fn inner_matches(&self, filename: impl AsRef<HgPath>) -> bool {
   163     fn inner_matches(&self, filename: &HgPath) -> bool {
   172         self.files.contains(filename.as_ref())
   164         self.files.contains(filename.as_ref())
   173     }
   165     }
   174 }
   166 }
   175 
   167 
   176 impl<'a> Matcher for FileMatcher<'a> {
   168 impl<'a> Matcher for FileMatcher<'a> {
   177     fn file_set(&self) -> Option<&HashSet<&HgPath>> {
   169     fn file_set(&self) -> Option<&HashSet<&HgPath>> {
   178         Some(&self.files)
   170         Some(&self.files)
   179     }
   171     }
   180     fn exact_match(&self, filename: impl AsRef<HgPath>) -> bool {
   172     fn exact_match(&self, filename: &HgPath) -> bool {
   181         self.inner_matches(filename)
   173         self.inner_matches(filename)
   182     }
   174     }
   183     fn matches(&self, filename: impl AsRef<HgPath>) -> bool {
   175     fn matches(&self, filename: &HgPath) -> bool {
   184         self.inner_matches(filename)
   176         self.inner_matches(filename)
   185     }
   177     }
   186     fn visit_children_set(
   178     fn visit_children_set(&self, directory: &HgPath) -> VisitChildrenSet {
   187         &self,
       
   188         directory: impl AsRef<HgPath>,
       
   189     ) -> VisitChildrenSet {
       
   190         if self.files.is_empty() || !self.dirs.contains(&directory) {
   179         if self.files.is_empty() || !self.dirs.contains(&directory) {
   191             return VisitChildrenSet::Empty;
   180             return VisitChildrenSet::Empty;
   192         }
   181         }
   193         let dirs_as_set = self.dirs.iter().map(Deref::deref).collect();
   182         let dirs_as_set = self.dirs.iter().map(Deref::deref).collect();
   194 
   183 
   268 impl<'a> Matcher for IncludeMatcher<'a> {
   257 impl<'a> Matcher for IncludeMatcher<'a> {
   269     fn file_set(&self) -> Option<&HashSet<&HgPath>> {
   258     fn file_set(&self) -> Option<&HashSet<&HgPath>> {
   270         None
   259         None
   271     }
   260     }
   272 
   261 
   273     fn exact_match(&self, _filename: impl AsRef<HgPath>) -> bool {
   262     fn exact_match(&self, _filename: &HgPath) -> bool {
   274         false
   263         false
   275     }
   264     }
   276 
   265 
   277     fn matches(&self, filename: impl AsRef<HgPath>) -> bool {
   266     fn matches(&self, filename: &HgPath) -> bool {
   278         (self.match_fn)(filename.as_ref())
   267         (self.match_fn)(filename.as_ref())
   279     }
   268     }
   280 
   269 
   281     fn visit_children_set(
   270     fn visit_children_set(&self, directory: &HgPath) -> VisitChildrenSet {
   282         &self,
       
   283         directory: impl AsRef<HgPath>,
       
   284     ) -> VisitChildrenSet {
       
   285         let dir = directory.as_ref();
   271         let dir = directory.as_ref();
   286         if self.prefix && self.roots.contains(dir) {
   272         if self.prefix && self.roots.contains(dir) {
   287             return VisitChildrenSet::Recursive;
   273             return VisitChildrenSet::Recursive;
   288         }
   274         }
   289         if self.roots.contains(HgPath::new(b""))
   275         if self.roots.contains(HgPath::new(b""))
   723     }
   709     }
   724 
   710 
   725     #[test]
   711     #[test]
   726     fn test_filematcher_visit_children_set() {
   712     fn test_filematcher_visit_children_set() {
   727         // Visitchildrenset
   713         // Visitchildrenset
   728         let files = vec![HgPath::new(b"dir/subdir/foo.txt")];
   714         let files = vec![HgPathBuf::from_bytes(b"dir/subdir/foo.txt")];
   729         let matcher = FileMatcher::new(&files).unwrap();
   715         let matcher = FileMatcher::new(&files).unwrap();
   730 
   716 
   731         let mut set = HashSet::new();
   717         let mut set = HashSet::new();
   732         set.insert(HgPath::new(b"dir"));
   718         set.insert(HgPath::new(b"dir"));
   733         assert_eq!(
   719         assert_eq!(
   764     }
   750     }
   765 
   751 
   766     #[test]
   752     #[test]
   767     fn test_filematcher_visit_children_set_files_and_dirs() {
   753     fn test_filematcher_visit_children_set_files_and_dirs() {
   768         let files = vec![
   754         let files = vec![
   769             HgPath::new(b"rootfile.txt"),
   755             HgPathBuf::from_bytes(b"rootfile.txt"),
   770             HgPath::new(b"a/file1.txt"),
   756             HgPathBuf::from_bytes(b"a/file1.txt"),
   771             HgPath::new(b"a/b/file2.txt"),
   757             HgPathBuf::from_bytes(b"a/b/file2.txt"),
   772             // No file in a/b/c
   758             // No file in a/b/c
   773             HgPath::new(b"a/b/c/d/file4.txt"),
   759             HgPathBuf::from_bytes(b"a/b/c/d/file4.txt"),
   774         ];
   760         ];
   775         let matcher = FileMatcher::new(&files).unwrap();
   761         let matcher = FileMatcher::new(&files).unwrap();
   776 
   762 
   777         let mut set = HashSet::new();
   763         let mut set = HashSet::new();
   778         set.insert(HgPath::new(b"a"));
   764         set.insert(HgPath::new(b"a"));