302 fn is_exact(&self) -> bool { |
302 fn is_exact(&self) -> bool { |
303 false |
303 false |
304 } |
304 } |
305 } |
305 } |
306 |
306 |
|
307 /// The union of multiple matchers. Will match if any of the matchers match. |
|
308 pub struct UnionMatcher { |
|
309 matchers: Vec<Box<dyn Matcher + Sync>>, |
|
310 } |
|
311 |
|
312 impl Matcher for UnionMatcher { |
|
313 fn file_set(&self) -> Option<&HashSet<HgPathBuf>> { |
|
314 None |
|
315 } |
|
316 |
|
317 fn exact_match(&self, _filename: &HgPath) -> bool { |
|
318 false |
|
319 } |
|
320 |
|
321 fn matches(&self, filename: &HgPath) -> bool { |
|
322 self.matchers.iter().any(|m| m.matches(filename)) |
|
323 } |
|
324 |
|
325 fn visit_children_set(&self, directory: &HgPath) -> VisitChildrenSet { |
|
326 let mut result = HashSet::new(); |
|
327 let mut this = false; |
|
328 for matcher in self.matchers.iter() { |
|
329 let visit = matcher.visit_children_set(directory); |
|
330 match visit { |
|
331 VisitChildrenSet::Empty => continue, |
|
332 VisitChildrenSet::This => { |
|
333 this = true; |
|
334 // Don't break, we might have an 'all' in here. |
|
335 continue; |
|
336 } |
|
337 VisitChildrenSet::Set(set) => { |
|
338 result.extend(set); |
|
339 } |
|
340 VisitChildrenSet::Recursive => { |
|
341 return visit; |
|
342 } |
|
343 } |
|
344 } |
|
345 if this { |
|
346 return VisitChildrenSet::This; |
|
347 } |
|
348 if result.is_empty() { |
|
349 VisitChildrenSet::Empty |
|
350 } else { |
|
351 VisitChildrenSet::Set(result) |
|
352 } |
|
353 } |
|
354 |
|
355 fn matches_everything(&self) -> bool { |
|
356 // TODO Maybe if all are AlwaysMatcher? |
|
357 false |
|
358 } |
|
359 |
|
360 fn is_exact(&self) -> bool { |
|
361 false |
|
362 } |
|
363 } |
|
364 |
|
365 impl UnionMatcher { |
|
366 pub fn new(matchers: Vec<Box<dyn Matcher + Sync>>) -> Self { |
|
367 Self { matchers } |
|
368 } |
|
369 } |
|
370 |
307 /// Returns a function that matches an `HgPath` against the given regex |
371 /// Returns a function that matches an `HgPath` against the given regex |
308 /// pattern. |
372 /// pattern. |
309 /// |
373 /// |
310 /// This can fail when the pattern is invalid or not supported by the |
374 /// This can fail when the pattern is invalid or not supported by the |
311 /// underlying engine (the `regex` crate), for instance anything with |
375 /// underlying engine (the `regex` crate), for instance anything with |
923 assert_eq!( |
987 assert_eq!( |
924 matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), |
988 matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), |
925 VisitChildrenSet::This |
989 VisitChildrenSet::This |
926 ); |
990 ); |
927 } |
991 } |
928 } |
992 |
|
993 #[test] |
|
994 fn test_unionmatcher() { |
|
995 // Path + Rootfiles |
|
996 let m1 = IncludeMatcher::new(vec![IgnorePattern::new( |
|
997 PatternSyntax::RelPath, |
|
998 b"dir/subdir", |
|
999 Path::new(""), |
|
1000 )]) |
|
1001 .unwrap(); |
|
1002 let m2 = IncludeMatcher::new(vec![IgnorePattern::new( |
|
1003 PatternSyntax::RootFiles, |
|
1004 b"dir", |
|
1005 Path::new(""), |
|
1006 )]) |
|
1007 .unwrap(); |
|
1008 let matcher = UnionMatcher::new(vec![Box::new(m1), Box::new(m2)]); |
|
1009 |
|
1010 let mut set = HashSet::new(); |
|
1011 set.insert(HgPathBuf::from_bytes(b"dir")); |
|
1012 assert_eq!( |
|
1013 matcher.visit_children_set(HgPath::new(b"")), |
|
1014 VisitChildrenSet::Set(set) |
|
1015 ); |
|
1016 assert_eq!( |
|
1017 matcher.visit_children_set(HgPath::new(b"dir")), |
|
1018 VisitChildrenSet::This |
|
1019 ); |
|
1020 assert_eq!( |
|
1021 matcher.visit_children_set(HgPath::new(b"dir/subdir")), |
|
1022 VisitChildrenSet::Recursive |
|
1023 ); |
|
1024 assert_eq!( |
|
1025 matcher.visit_children_set(HgPath::new(b"dir/foo")), |
|
1026 VisitChildrenSet::Empty |
|
1027 ); |
|
1028 assert_eq!( |
|
1029 matcher.visit_children_set(HgPath::new(b"folder")), |
|
1030 VisitChildrenSet::Empty |
|
1031 ); |
|
1032 assert_eq!( |
|
1033 matcher.visit_children_set(HgPath::new(b"folder")), |
|
1034 VisitChildrenSet::Empty |
|
1035 ); |
|
1036 |
|
1037 // OPT: These next two could be 'all' instead of 'this'. |
|
1038 assert_eq!( |
|
1039 matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), |
|
1040 VisitChildrenSet::This |
|
1041 ); |
|
1042 assert_eq!( |
|
1043 matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), |
|
1044 VisitChildrenSet::This |
|
1045 ); |
|
1046 |
|
1047 // Path + unrelated Path |
|
1048 let m1 = IncludeMatcher::new(vec![IgnorePattern::new( |
|
1049 PatternSyntax::RelPath, |
|
1050 b"dir/subdir", |
|
1051 Path::new(""), |
|
1052 )]) |
|
1053 .unwrap(); |
|
1054 let m2 = IncludeMatcher::new(vec![IgnorePattern::new( |
|
1055 PatternSyntax::RelPath, |
|
1056 b"folder", |
|
1057 Path::new(""), |
|
1058 )]) |
|
1059 .unwrap(); |
|
1060 let matcher = UnionMatcher::new(vec![Box::new(m1), Box::new(m2)]); |
|
1061 |
|
1062 let mut set = HashSet::new(); |
|
1063 set.insert(HgPathBuf::from_bytes(b"folder")); |
|
1064 set.insert(HgPathBuf::from_bytes(b"dir")); |
|
1065 assert_eq!( |
|
1066 matcher.visit_children_set(HgPath::new(b"")), |
|
1067 VisitChildrenSet::Set(set) |
|
1068 ); |
|
1069 let mut set = HashSet::new(); |
|
1070 set.insert(HgPathBuf::from_bytes(b"subdir")); |
|
1071 assert_eq!( |
|
1072 matcher.visit_children_set(HgPath::new(b"dir")), |
|
1073 VisitChildrenSet::Set(set) |
|
1074 ); |
|
1075 |
|
1076 assert_eq!( |
|
1077 matcher.visit_children_set(HgPath::new(b"dir/subdir")), |
|
1078 VisitChildrenSet::Recursive |
|
1079 ); |
|
1080 assert_eq!( |
|
1081 matcher.visit_children_set(HgPath::new(b"dir/foo")), |
|
1082 VisitChildrenSet::Empty |
|
1083 ); |
|
1084 |
|
1085 assert_eq!( |
|
1086 matcher.visit_children_set(HgPath::new(b"folder")), |
|
1087 VisitChildrenSet::Recursive |
|
1088 ); |
|
1089 // OPT: These next two could be 'all' instead of 'this'. |
|
1090 assert_eq!( |
|
1091 matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), |
|
1092 VisitChildrenSet::This |
|
1093 ); |
|
1094 assert_eq!( |
|
1095 matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), |
|
1096 VisitChildrenSet::This |
|
1097 ); |
|
1098 |
|
1099 // Path + subpath |
|
1100 let m1 = IncludeMatcher::new(vec![IgnorePattern::new( |
|
1101 PatternSyntax::RelPath, |
|
1102 b"dir/subdir/x", |
|
1103 Path::new(""), |
|
1104 )]) |
|
1105 .unwrap(); |
|
1106 let m2 = IncludeMatcher::new(vec![IgnorePattern::new( |
|
1107 PatternSyntax::RelPath, |
|
1108 b"dir/subdir", |
|
1109 Path::new(""), |
|
1110 )]) |
|
1111 .unwrap(); |
|
1112 let matcher = UnionMatcher::new(vec![Box::new(m1), Box::new(m2)]); |
|
1113 |
|
1114 let mut set = HashSet::new(); |
|
1115 set.insert(HgPathBuf::from_bytes(b"dir")); |
|
1116 assert_eq!( |
|
1117 matcher.visit_children_set(HgPath::new(b"")), |
|
1118 VisitChildrenSet::Set(set) |
|
1119 ); |
|
1120 let mut set = HashSet::new(); |
|
1121 set.insert(HgPathBuf::from_bytes(b"subdir")); |
|
1122 assert_eq!( |
|
1123 matcher.visit_children_set(HgPath::new(b"dir")), |
|
1124 VisitChildrenSet::Set(set) |
|
1125 ); |
|
1126 |
|
1127 assert_eq!( |
|
1128 matcher.visit_children_set(HgPath::new(b"dir/subdir")), |
|
1129 VisitChildrenSet::Recursive |
|
1130 ); |
|
1131 assert_eq!( |
|
1132 matcher.visit_children_set(HgPath::new(b"dir/foo")), |
|
1133 VisitChildrenSet::Empty |
|
1134 ); |
|
1135 |
|
1136 assert_eq!( |
|
1137 matcher.visit_children_set(HgPath::new(b"folder")), |
|
1138 VisitChildrenSet::Empty |
|
1139 ); |
|
1140 assert_eq!( |
|
1141 matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), |
|
1142 VisitChildrenSet::Recursive |
|
1143 ); |
|
1144 // OPT: this should probably be 'all' not 'this'. |
|
1145 assert_eq!( |
|
1146 matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), |
|
1147 VisitChildrenSet::This |
|
1148 ); |
|
1149 } |
|
1150 } |