rust/hg-core/src/matchers.rs
changeset 49478 d8ce883ff1f4
parent 49464 90512ca6a255
child 49486 e8481625c582
equal deleted inserted replaced
49477:f3cd2d6eeef9 49478:d8ce883ff1f4
   472         };
   472         };
   473         Self { m1, m2, files }
   473         Self { m1, m2, files }
   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 ///
   480 /// This can fail when the pattern is invalid or not supported by the
   564 /// This can fail when the pattern is invalid or not supported by the
   481 /// underlying engine (the `regex` crate), for instance anything with
   565 /// underlying engine (the `regex` crate), for instance anything with
  1487         assert_eq!(
  1571         assert_eq!(
  1488             matcher.visit_children_set(HgPath::new(b"dir/subdir/x")),
  1572             matcher.visit_children_set(HgPath::new(b"dir/subdir/x")),
  1489             VisitChildrenSet::Empty
  1573             VisitChildrenSet::Empty
  1490         );
  1574         );
  1491     }
  1575     }
  1492 }
  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     }
       
  1673 }