366 pub fn new(matchers: Vec<Box<dyn Matcher + Sync>>) -> Self { |
366 pub fn new(matchers: Vec<Box<dyn Matcher + Sync>>) -> Self { |
367 Self { matchers } |
367 Self { matchers } |
368 } |
368 } |
369 } |
369 } |
370 |
370 |
|
371 pub struct IntersectionMatcher { |
|
372 m1: Box<dyn Matcher + Sync>, |
|
373 m2: Box<dyn Matcher + Sync>, |
|
374 files: Option<HashSet<HgPathBuf>>, |
|
375 } |
|
376 |
|
377 impl Matcher for IntersectionMatcher { |
|
378 fn file_set(&self) -> Option<&HashSet<HgPathBuf>> { |
|
379 self.files.as_ref() |
|
380 } |
|
381 |
|
382 fn exact_match(&self, filename: &HgPath) -> bool { |
|
383 self.files.as_ref().map_or(false, |f| f.contains(filename)) |
|
384 } |
|
385 |
|
386 fn matches(&self, filename: &HgPath) -> bool { |
|
387 self.m1.matches(filename) && self.m2.matches(filename) |
|
388 } |
|
389 |
|
390 fn visit_children_set(&self, directory: &HgPath) -> VisitChildrenSet { |
|
391 let m1_set = self.m1.visit_children_set(directory); |
|
392 if m1_set == VisitChildrenSet::Empty { |
|
393 return VisitChildrenSet::Empty; |
|
394 } |
|
395 let m2_set = self.m2.visit_children_set(directory); |
|
396 if m2_set == VisitChildrenSet::Empty { |
|
397 return VisitChildrenSet::Empty; |
|
398 } |
|
399 |
|
400 if m1_set == VisitChildrenSet::Recursive { |
|
401 return m2_set; |
|
402 } else if m2_set == VisitChildrenSet::Recursive { |
|
403 return m1_set; |
|
404 } |
|
405 |
|
406 match (&m1_set, &m2_set) { |
|
407 (VisitChildrenSet::Recursive, _) => m2_set, |
|
408 (_, VisitChildrenSet::Recursive) => m1_set, |
|
409 (VisitChildrenSet::This, _) | (_, VisitChildrenSet::This) => { |
|
410 VisitChildrenSet::This |
|
411 } |
|
412 (VisitChildrenSet::Set(m1), VisitChildrenSet::Set(m2)) => { |
|
413 let set: HashSet<_> = m1.intersection(&m2).cloned().collect(); |
|
414 if set.is_empty() { |
|
415 VisitChildrenSet::Empty |
|
416 } else { |
|
417 VisitChildrenSet::Set(set) |
|
418 } |
|
419 } |
|
420 _ => unreachable!(), |
|
421 } |
|
422 } |
|
423 |
|
424 fn matches_everything(&self) -> bool { |
|
425 self.m1.matches_everything() && self.m2.matches_everything() |
|
426 } |
|
427 |
|
428 fn is_exact(&self) -> bool { |
|
429 self.m1.is_exact() || self.m2.is_exact() |
|
430 } |
|
431 } |
|
432 |
|
433 impl IntersectionMatcher { |
|
434 pub fn new( |
|
435 mut m1: Box<dyn Matcher + Sync>, |
|
436 mut m2: Box<dyn Matcher + Sync>, |
|
437 ) -> Self { |
|
438 let files = if m1.is_exact() || m2.is_exact() { |
|
439 if !m1.is_exact() { |
|
440 std::mem::swap(&mut m1, &mut m2); |
|
441 } |
|
442 m1.file_set().map(|m1_files| { |
|
443 m1_files.iter().cloned().filter(|f| m2.matches(f)).collect() |
|
444 }) |
|
445 } else { |
|
446 None |
|
447 }; |
|
448 Self { m1, m2, files } |
|
449 } |
|
450 } |
|
451 |
371 /// Returns a function that matches an `HgPath` against the given regex |
452 /// Returns a function that matches an `HgPath` against the given regex |
372 /// pattern. |
453 /// pattern. |
373 /// |
454 /// |
374 /// This can fail when the pattern is invalid or not supported by the |
455 /// This can fail when the pattern is invalid or not supported by the |
375 /// underlying engine (the `regex` crate), for instance anything with |
456 /// underlying engine (the `regex` crate), for instance anything with |
1145 assert_eq!( |
1226 assert_eq!( |
1146 matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), |
1227 matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), |
1147 VisitChildrenSet::This |
1228 VisitChildrenSet::This |
1148 ); |
1229 ); |
1149 } |
1230 } |
1150 } |
1231 |
|
1232 #[test] |
|
1233 fn test_intersectionmatcher() { |
|
1234 // Include path + Include rootfiles |
|
1235 let m1 = Box::new( |
|
1236 IncludeMatcher::new(vec![IgnorePattern::new( |
|
1237 PatternSyntax::RelPath, |
|
1238 b"dir/subdir", |
|
1239 Path::new(""), |
|
1240 )]) |
|
1241 .unwrap(), |
|
1242 ); |
|
1243 let m2 = Box::new( |
|
1244 IncludeMatcher::new(vec![IgnorePattern::new( |
|
1245 PatternSyntax::RootFiles, |
|
1246 b"dir", |
|
1247 Path::new(""), |
|
1248 )]) |
|
1249 .unwrap(), |
|
1250 ); |
|
1251 let matcher = IntersectionMatcher::new(m1, m2); |
|
1252 |
|
1253 let mut set = HashSet::new(); |
|
1254 set.insert(HgPathBuf::from_bytes(b"dir")); |
|
1255 assert_eq!( |
|
1256 matcher.visit_children_set(HgPath::new(b"")), |
|
1257 VisitChildrenSet::Set(set) |
|
1258 ); |
|
1259 assert_eq!( |
|
1260 matcher.visit_children_set(HgPath::new(b"dir")), |
|
1261 VisitChildrenSet::This |
|
1262 ); |
|
1263 assert_eq!( |
|
1264 matcher.visit_children_set(HgPath::new(b"dir/subdir")), |
|
1265 VisitChildrenSet::Empty |
|
1266 ); |
|
1267 assert_eq!( |
|
1268 matcher.visit_children_set(HgPath::new(b"dir/foo")), |
|
1269 VisitChildrenSet::Empty |
|
1270 ); |
|
1271 assert_eq!( |
|
1272 matcher.visit_children_set(HgPath::new(b"folder")), |
|
1273 VisitChildrenSet::Empty |
|
1274 ); |
|
1275 assert_eq!( |
|
1276 matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), |
|
1277 VisitChildrenSet::Empty |
|
1278 ); |
|
1279 assert_eq!( |
|
1280 matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), |
|
1281 VisitChildrenSet::Empty |
|
1282 ); |
|
1283 |
|
1284 // Non intersecting paths |
|
1285 let m1 = Box::new( |
|
1286 IncludeMatcher::new(vec![IgnorePattern::new( |
|
1287 PatternSyntax::RelPath, |
|
1288 b"dir/subdir", |
|
1289 Path::new(""), |
|
1290 )]) |
|
1291 .unwrap(), |
|
1292 ); |
|
1293 let m2 = Box::new( |
|
1294 IncludeMatcher::new(vec![IgnorePattern::new( |
|
1295 PatternSyntax::RelPath, |
|
1296 b"folder", |
|
1297 Path::new(""), |
|
1298 )]) |
|
1299 .unwrap(), |
|
1300 ); |
|
1301 let matcher = IntersectionMatcher::new(m1, m2); |
|
1302 |
|
1303 assert_eq!( |
|
1304 matcher.visit_children_set(HgPath::new(b"")), |
|
1305 VisitChildrenSet::Empty |
|
1306 ); |
|
1307 assert_eq!( |
|
1308 matcher.visit_children_set(HgPath::new(b"dir")), |
|
1309 VisitChildrenSet::Empty |
|
1310 ); |
|
1311 assert_eq!( |
|
1312 matcher.visit_children_set(HgPath::new(b"dir/subdir")), |
|
1313 VisitChildrenSet::Empty |
|
1314 ); |
|
1315 assert_eq!( |
|
1316 matcher.visit_children_set(HgPath::new(b"dir/foo")), |
|
1317 VisitChildrenSet::Empty |
|
1318 ); |
|
1319 assert_eq!( |
|
1320 matcher.visit_children_set(HgPath::new(b"folder")), |
|
1321 VisitChildrenSet::Empty |
|
1322 ); |
|
1323 assert_eq!( |
|
1324 matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), |
|
1325 VisitChildrenSet::Empty |
|
1326 ); |
|
1327 assert_eq!( |
|
1328 matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), |
|
1329 VisitChildrenSet::Empty |
|
1330 ); |
|
1331 |
|
1332 // Nested paths |
|
1333 let m1 = Box::new( |
|
1334 IncludeMatcher::new(vec![IgnorePattern::new( |
|
1335 PatternSyntax::RelPath, |
|
1336 b"dir/subdir/x", |
|
1337 Path::new(""), |
|
1338 )]) |
|
1339 .unwrap(), |
|
1340 ); |
|
1341 let m2 = Box::new( |
|
1342 IncludeMatcher::new(vec![IgnorePattern::new( |
|
1343 PatternSyntax::RelPath, |
|
1344 b"dir/subdir", |
|
1345 Path::new(""), |
|
1346 )]) |
|
1347 .unwrap(), |
|
1348 ); |
|
1349 let matcher = IntersectionMatcher::new(m1, m2); |
|
1350 |
|
1351 let mut set = HashSet::new(); |
|
1352 set.insert(HgPathBuf::from_bytes(b"dir")); |
|
1353 assert_eq!( |
|
1354 matcher.visit_children_set(HgPath::new(b"")), |
|
1355 VisitChildrenSet::Set(set) |
|
1356 ); |
|
1357 |
|
1358 let mut set = HashSet::new(); |
|
1359 set.insert(HgPathBuf::from_bytes(b"subdir")); |
|
1360 assert_eq!( |
|
1361 matcher.visit_children_set(HgPath::new(b"dir")), |
|
1362 VisitChildrenSet::Set(set) |
|
1363 ); |
|
1364 let mut set = HashSet::new(); |
|
1365 set.insert(HgPathBuf::from_bytes(b"x")); |
|
1366 assert_eq!( |
|
1367 matcher.visit_children_set(HgPath::new(b"dir/subdir")), |
|
1368 VisitChildrenSet::Set(set) |
|
1369 ); |
|
1370 assert_eq!( |
|
1371 matcher.visit_children_set(HgPath::new(b"dir/foo")), |
|
1372 VisitChildrenSet::Empty |
|
1373 ); |
|
1374 assert_eq!( |
|
1375 matcher.visit_children_set(HgPath::new(b"folder")), |
|
1376 VisitChildrenSet::Empty |
|
1377 ); |
|
1378 assert_eq!( |
|
1379 matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), |
|
1380 VisitChildrenSet::Empty |
|
1381 ); |
|
1382 // OPT: this should probably be 'all' not 'this'. |
|
1383 assert_eq!( |
|
1384 matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), |
|
1385 VisitChildrenSet::This |
|
1386 ); |
|
1387 |
|
1388 // Diverging paths |
|
1389 let m1 = Box::new( |
|
1390 IncludeMatcher::new(vec![IgnorePattern::new( |
|
1391 PatternSyntax::RelPath, |
|
1392 b"dir/subdir/x", |
|
1393 Path::new(""), |
|
1394 )]) |
|
1395 .unwrap(), |
|
1396 ); |
|
1397 let m2 = Box::new( |
|
1398 IncludeMatcher::new(vec![IgnorePattern::new( |
|
1399 PatternSyntax::RelPath, |
|
1400 b"dir/subdir/z", |
|
1401 Path::new(""), |
|
1402 )]) |
|
1403 .unwrap(), |
|
1404 ); |
|
1405 let matcher = IntersectionMatcher::new(m1, m2); |
|
1406 |
|
1407 // OPT: these next two could probably be Empty as well. |
|
1408 let mut set = HashSet::new(); |
|
1409 set.insert(HgPathBuf::from_bytes(b"dir")); |
|
1410 assert_eq!( |
|
1411 matcher.visit_children_set(HgPath::new(b"")), |
|
1412 VisitChildrenSet::Set(set) |
|
1413 ); |
|
1414 // OPT: these next two could probably be Empty as well. |
|
1415 let mut set = HashSet::new(); |
|
1416 set.insert(HgPathBuf::from_bytes(b"subdir")); |
|
1417 assert_eq!( |
|
1418 matcher.visit_children_set(HgPath::new(b"dir")), |
|
1419 VisitChildrenSet::Set(set) |
|
1420 ); |
|
1421 assert_eq!( |
|
1422 matcher.visit_children_set(HgPath::new(b"dir/subdir")), |
|
1423 VisitChildrenSet::Empty |
|
1424 ); |
|
1425 assert_eq!( |
|
1426 matcher.visit_children_set(HgPath::new(b"dir/foo")), |
|
1427 VisitChildrenSet::Empty |
|
1428 ); |
|
1429 assert_eq!( |
|
1430 matcher.visit_children_set(HgPath::new(b"folder")), |
|
1431 VisitChildrenSet::Empty |
|
1432 ); |
|
1433 assert_eq!( |
|
1434 matcher.visit_children_set(HgPath::new(b"dir/subdir/z")), |
|
1435 VisitChildrenSet::Empty |
|
1436 ); |
|
1437 assert_eq!( |
|
1438 matcher.visit_children_set(HgPath::new(b"dir/subdir/x")), |
|
1439 VisitChildrenSet::Empty |
|
1440 ); |
|
1441 } |
|
1442 } |