##// END OF EJS Templates
rust-matchers: implement DifferenceMatcher...
Raphaël Gomès -
r50373:d8ce883f default
parent child Browse files
Show More
@@ -474,6 +474,90 impl IntersectionMatcher {
474 }
474 }
475 }
475 }
476
476
477 pub struct DifferenceMatcher {
478 base: Box<dyn Matcher + Sync>,
479 excluded: Box<dyn Matcher + Sync>,
480 files: Option<HashSet<HgPathBuf>>,
481 }
482
483 impl Matcher for DifferenceMatcher {
484 fn file_set(&self) -> Option<&HashSet<HgPathBuf>> {
485 self.files.as_ref()
486 }
487
488 fn exact_match(&self, filename: &HgPath) -> bool {
489 self.files.as_ref().map_or(false, |f| f.contains(filename))
490 }
491
492 fn matches(&self, filename: &HgPath) -> bool {
493 self.base.matches(filename) && !self.excluded.matches(filename)
494 }
495
496 fn visit_children_set(&self, directory: &HgPath) -> VisitChildrenSet {
497 let excluded_set = self.excluded.visit_children_set(directory);
498 if excluded_set == VisitChildrenSet::Recursive {
499 return VisitChildrenSet::Empty;
500 }
501 let base_set = self.base.visit_children_set(directory);
502 // Possible values for base: 'recursive', 'this', set(...), set()
503 // Possible values for excluded: 'this', set(...), set()
504 // If excluded has nothing under here that we care about, return base,
505 // even if it's 'recursive'.
506 if excluded_set == VisitChildrenSet::Empty {
507 return base_set;
508 }
509 match base_set {
510 VisitChildrenSet::This | VisitChildrenSet::Recursive => {
511 // Never return 'recursive' here if excluded_set is any kind of
512 // non-empty (either 'this' or set(foo)), since excluded might
513 // return set() for a subdirectory.
514 VisitChildrenSet::This
515 }
516 set => {
517 // Possible values for base: set(...), set()
518 // Possible values for excluded: 'this', set(...)
519 // We ignore excluded set results. They're possibly incorrect:
520 // base = path:dir/subdir
521 // excluded=rootfilesin:dir,
522 // visit_children_set(''):
523 // base returns {'dir'}, excluded returns {'dir'}, if we
524 // subtracted we'd return set(), which is *not* correct, we
525 // still need to visit 'dir'!
526 set
527 }
528 }
529 }
530
531 fn matches_everything(&self) -> bool {
532 false
533 }
534
535 fn is_exact(&self) -> bool {
536 self.base.is_exact()
537 }
538 }
539
540 impl DifferenceMatcher {
541 pub fn new(
542 base: Box<dyn Matcher + Sync>,
543 excluded: Box<dyn Matcher + Sync>,
544 ) -> Self {
545 let base_is_exact = base.is_exact();
546 let base_files = base.file_set().map(ToOwned::to_owned);
547 let mut new = Self {
548 base,
549 excluded,
550 files: None,
551 };
552 if base_is_exact {
553 new.files = base_files.map(|files| {
554 files.iter().cloned().filter(|f| new.matches(f)).collect()
555 });
556 }
557 new
558 }
559 }
560
477 /// Returns a function that matches an `HgPath` against the given regex
561 /// Returns a function that matches an `HgPath` against the given regex
478 /// pattern.
562 /// pattern.
479 ///
563 ///
@@ -1489,4 +1573,101 mod tests {
1489 VisitChildrenSet::Empty
1573 VisitChildrenSet::Empty
1490 );
1574 );
1491 }
1575 }
1576
1577 #[test]
1578 fn test_differencematcher() {
1579 // Two alwaysmatchers should function like a nevermatcher
1580 let m1 = AlwaysMatcher;
1581 let m2 = AlwaysMatcher;
1582 let matcher = DifferenceMatcher::new(Box::new(m1), Box::new(m2));
1583
1584 for case in &[
1585 &b""[..],
1586 b"dir",
1587 b"dir/subdir",
1588 b"dir/subdir/z",
1589 b"dir/foo",
1590 b"dir/subdir/x",
1591 b"folder",
1592 ] {
1593 assert_eq!(
1594 matcher.visit_children_set(HgPath::new(case)),
1595 VisitChildrenSet::Empty
1596 );
1597 }
1598
1599 // One always and one never should behave the same as an always
1600 let m1 = AlwaysMatcher;
1601 let m2 = NeverMatcher;
1602 let matcher = DifferenceMatcher::new(Box::new(m1), Box::new(m2));
1603
1604 for case in &[
1605 &b""[..],
1606 b"dir",
1607 b"dir/subdir",
1608 b"dir/subdir/z",
1609 b"dir/foo",
1610 b"dir/subdir/x",
1611 b"folder",
1612 ] {
1613 assert_eq!(
1614 matcher.visit_children_set(HgPath::new(case)),
1615 VisitChildrenSet::Recursive
1616 );
1617 }
1618
1619 // Two include matchers
1620 let m1 = Box::new(
1621 IncludeMatcher::new(vec![IgnorePattern::new(
1622 PatternSyntax::RelPath,
1623 b"dir/subdir",
1624 Path::new("/repo"),
1625 )])
1626 .unwrap(),
1627 );
1628 let m2 = Box::new(
1629 IncludeMatcher::new(vec![IgnorePattern::new(
1630 PatternSyntax::RootFiles,
1631 b"dir",
1632 Path::new("/repo"),
1633 )])
1634 .unwrap(),
1635 );
1636
1637 let matcher = DifferenceMatcher::new(m1, m2);
1638
1639 let mut set = HashSet::new();
1640 set.insert(HgPathBuf::from_bytes(b"dir"));
1641 assert_eq!(
1642 matcher.visit_children_set(HgPath::new(b"")),
1643 VisitChildrenSet::Set(set)
1644 );
1645
1646 let mut set = HashSet::new();
1647 set.insert(HgPathBuf::from_bytes(b"subdir"));
1648 assert_eq!(
1649 matcher.visit_children_set(HgPath::new(b"dir")),
1650 VisitChildrenSet::Set(set)
1651 );
1652 assert_eq!(
1653 matcher.visit_children_set(HgPath::new(b"dir/subdir")),
1654 VisitChildrenSet::Recursive
1655 );
1656 assert_eq!(
1657 matcher.visit_children_set(HgPath::new(b"dir/foo")),
1658 VisitChildrenSet::Empty
1659 );
1660 assert_eq!(
1661 matcher.visit_children_set(HgPath::new(b"folder")),
1662 VisitChildrenSet::Empty
1663 );
1664 assert_eq!(
1665 matcher.visit_children_set(HgPath::new(b"dir/subdir/z")),
1666 VisitChildrenSet::This
1667 );
1668 assert_eq!(
1669 matcher.visit_children_set(HgPath::new(b"dir/subdir/x")),
1670 VisitChildrenSet::This
1671 );
1672 }
1492 }
1673 }
General Comments 0
You need to be logged in to leave comments. Login now