rust/hg-core/src/matchers.rs
changeset 49350 5e53ecbc308f
parent 49348 b508cffd3c04
child 49352 97dcd6906e6f
equal deleted inserted replaced
49349:0043c7aa3250 49350:5e53ecbc308f
   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 }