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