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")); |