rust/hg-core/src/matchers.rs
changeset 50889 f874342fa568
parent 50885 090658724abf
child 51113 687e192dae16
equal deleted inserted replaced
50888:76387f79befe 50889:f874342fa568
   248     fn is_exact(&self) -> bool {
   248     fn is_exact(&self) -> bool {
   249         true
   249         true
   250     }
   250     }
   251 }
   251 }
   252 
   252 
       
   253 /// Matches a set of (kind, pat, source) against a 'root' directory.
       
   254 /// (Currently the 'root' directory is effectively always empty)
       
   255 /// ```
       
   256 /// use hg::{
       
   257 ///     matchers::{PatternMatcher, Matcher},
       
   258 ///     IgnorePattern,
       
   259 ///     PatternSyntax,
       
   260 ///     utils::hg_path::{HgPath, HgPathBuf}
       
   261 /// };
       
   262 /// use std::collections::HashSet;
       
   263 /// use std::path::Path;
       
   264 /// ///
       
   265 /// let ignore_patterns : Vec<IgnorePattern> =
       
   266 ///     vec![IgnorePattern::new(PatternSyntax::Regexp, br".*\.c$", Path::new("")),
       
   267 ///          IgnorePattern::new(PatternSyntax::Path, b"foo/a", Path::new("")),
       
   268 ///          IgnorePattern::new(PatternSyntax::RelPath, b"b", Path::new("")),
       
   269 ///          IgnorePattern::new(PatternSyntax::Glob, b"*.h", Path::new("")),
       
   270 ///     ];
       
   271 /// let matcher = PatternMatcher::new(ignore_patterns).unwrap();
       
   272 /// ///
       
   273 /// assert_eq!(matcher.matches(HgPath::new(b"main.c")), true); // matches re:.*\.c$
       
   274 /// assert_eq!(matcher.matches(HgPath::new(b"b.txt")), false);
       
   275 /// assert_eq!(matcher.matches(HgPath::new(b"foo/a")), true); // matches path:foo/a
       
   276 /// assert_eq!(matcher.matches(HgPath::new(b"a")), false); // does not match path:b, since 'root' is 'foo'
       
   277 /// assert_eq!(matcher.matches(HgPath::new(b"b")), true); // matches relpath:b, since 'root' is 'foo'
       
   278 /// assert_eq!(matcher.matches(HgPath::new(b"lib.h")), true); // matches glob:*.h
       
   279 /// assert_eq!(matcher.file_set().unwrap(),
       
   280 ///            &HashSet::from([HgPathBuf::from_bytes(b""), HgPathBuf::from_bytes(b"foo/a"),
       
   281 ///                            HgPathBuf::from_bytes(b""), HgPathBuf::from_bytes(b"b")]));
       
   282 /// assert_eq!(matcher.exact_match(HgPath::new(b"foo/a")), true);
       
   283 /// assert_eq!(matcher.exact_match(HgPath::new(b"b")), true);
       
   284 /// assert_eq!(matcher.exact_match(HgPath::new(b"lib.h")), false); // exact matches are for (rel)path kinds
       
   285 /// ```
       
   286 pub struct PatternMatcher<'a> {
       
   287     patterns: Vec<u8>,
       
   288     match_fn: IgnoreFnType<'a>,
       
   289     /// Whether all the patterns match a prefix (i.e. recursively)
       
   290     prefix: bool,
       
   291     files: HashSet<HgPathBuf>,
       
   292     dirs: DirsMultiset,
       
   293 }
       
   294 
       
   295 impl core::fmt::Debug for PatternMatcher<'_> {
       
   296     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
       
   297         f.debug_struct("PatternMatcher")
       
   298             .field("patterns", &String::from_utf8_lossy(&self.patterns))
       
   299             .field("prefix", &self.prefix)
       
   300             .field("files", &self.files)
       
   301             .field("dirs", &self.dirs)
       
   302             .finish()
       
   303     }
       
   304 }
       
   305 
       
   306 impl<'a> PatternMatcher<'a> {
       
   307     pub fn new(ignore_patterns: Vec<IgnorePattern>) -> PatternResult<Self> {
       
   308         let (files, _) = roots_and_dirs(&ignore_patterns);
       
   309         let dirs = DirsMultiset::from_manifest(&files)?;
       
   310         let files: HashSet<HgPathBuf> = HashSet::from_iter(files.into_iter());
       
   311 
       
   312         let prefix = ignore_patterns.iter().all(|k| {
       
   313             matches!(k.syntax, PatternSyntax::Path | PatternSyntax::RelPath)
       
   314         });
       
   315         let (patterns, match_fn) = build_match(ignore_patterns, b"$")?;
       
   316 
       
   317         Ok(Self {
       
   318             patterns,
       
   319             match_fn,
       
   320             prefix,
       
   321             files,
       
   322             dirs,
       
   323         })
       
   324     }
       
   325 }
       
   326 
       
   327 impl<'a> Matcher for PatternMatcher<'a> {
       
   328     fn file_set(&self) -> Option<&HashSet<HgPathBuf>> {
       
   329         Some(&self.files)
       
   330     }
       
   331 
       
   332     fn exact_match(&self, filename: &HgPath) -> bool {
       
   333         self.files.contains(filename)
       
   334     }
       
   335 
       
   336     fn matches(&self, filename: &HgPath) -> bool {
       
   337         if self.files.contains(filename) {
       
   338             return true;
       
   339         }
       
   340         (self.match_fn)(filename)
       
   341     }
       
   342 
       
   343     fn visit_children_set(&self, directory: &HgPath) -> VisitChildrenSet {
       
   344         if self.prefix && self.files.contains(directory) {
       
   345             return VisitChildrenSet::Recursive;
       
   346         }
       
   347         let path_or_parents_in_set = find_dirs(directory)
       
   348             .any(|parent_dir| self.files.contains(parent_dir));
       
   349         if self.dirs.contains(directory) || path_or_parents_in_set {
       
   350             VisitChildrenSet::This
       
   351         } else {
       
   352             VisitChildrenSet::Empty
       
   353         }
       
   354     }
       
   355 
       
   356     fn matches_everything(&self) -> bool {
       
   357         false
       
   358     }
       
   359 
       
   360     fn is_exact(&self) -> bool {
       
   361         false
       
   362     }
       
   363 }
       
   364 
   253 /// Matches files that are included in the ignore rules.
   365 /// Matches files that are included in the ignore rules.
   254 /// ```
   366 /// ```
   255 /// use hg::{
   367 /// use hg::{
   256 ///     matchers::{IncludeMatcher, Matcher},
   368 ///     matchers::{IncludeMatcher, Matcher},
   257 ///     IgnorePattern,
   369 ///     IgnorePattern,
  1120             VisitChildrenSet::Empty
  1232             VisitChildrenSet::Empty
  1121         );
  1233         );
  1122     }
  1234     }
  1123 
  1235 
  1124     #[test]
  1236     #[test]
       
  1237     fn test_patternmatcher() {
       
  1238         // VisitdirPrefix
       
  1239         let m = PatternMatcher::new(vec![IgnorePattern::new(
       
  1240             PatternSyntax::Path,
       
  1241             b"dir/subdir",
       
  1242             Path::new(""),
       
  1243         )])
       
  1244         .unwrap();
       
  1245         assert_eq!(
       
  1246             m.visit_children_set(HgPath::new(b"")),
       
  1247             VisitChildrenSet::This
       
  1248         );
       
  1249         assert_eq!(
       
  1250             m.visit_children_set(HgPath::new(b"dir")),
       
  1251             VisitChildrenSet::This
       
  1252         );
       
  1253         assert_eq!(
       
  1254             m.visit_children_set(HgPath::new(b"dir/subdir")),
       
  1255             VisitChildrenSet::Recursive
       
  1256         );
       
  1257         // OPT: This should probably be Recursive if its parent is?
       
  1258         assert_eq!(
       
  1259             m.visit_children_set(HgPath::new(b"dir/subdir/x")),
       
  1260             VisitChildrenSet::This
       
  1261         );
       
  1262         assert_eq!(
       
  1263             m.visit_children_set(HgPath::new(b"folder")),
       
  1264             VisitChildrenSet::Empty
       
  1265         );
       
  1266 
       
  1267         // VisitchildrensetPrefix
       
  1268         let m = PatternMatcher::new(vec![IgnorePattern::new(
       
  1269             PatternSyntax::Path,
       
  1270             b"dir/subdir",
       
  1271             Path::new(""),
       
  1272         )])
       
  1273         .unwrap();
       
  1274         assert_eq!(
       
  1275             m.visit_children_set(HgPath::new(b"")),
       
  1276             VisitChildrenSet::This
       
  1277         );
       
  1278         assert_eq!(
       
  1279             m.visit_children_set(HgPath::new(b"dir")),
       
  1280             VisitChildrenSet::This
       
  1281         );
       
  1282         assert_eq!(
       
  1283             m.visit_children_set(HgPath::new(b"dir/subdir")),
       
  1284             VisitChildrenSet::Recursive
       
  1285         );
       
  1286         // OPT: This should probably be Recursive if its parent is?
       
  1287         assert_eq!(
       
  1288             m.visit_children_set(HgPath::new(b"dir/subdir/x")),
       
  1289             VisitChildrenSet::This
       
  1290         );
       
  1291         assert_eq!(
       
  1292             m.visit_children_set(HgPath::new(b"folder")),
       
  1293             VisitChildrenSet::Empty
       
  1294         );
       
  1295 
       
  1296         // VisitdirRootfilesin
       
  1297         let m = PatternMatcher::new(vec![IgnorePattern::new(
       
  1298             PatternSyntax::RootFiles,
       
  1299             b"dir/subdir",
       
  1300             Path::new(""),
       
  1301         )])
       
  1302         .unwrap();
       
  1303         assert_eq!(
       
  1304             m.visit_children_set(HgPath::new(b"dir/subdir/x")),
       
  1305             VisitChildrenSet::Empty
       
  1306         );
       
  1307         assert_eq!(
       
  1308             m.visit_children_set(HgPath::new(b"folder")),
       
  1309             VisitChildrenSet::Empty
       
  1310         );
       
  1311         // FIXME: These should probably be This.
       
  1312         assert_eq!(
       
  1313             m.visit_children_set(HgPath::new(b"")),
       
  1314             VisitChildrenSet::Empty
       
  1315         );
       
  1316         assert_eq!(
       
  1317             m.visit_children_set(HgPath::new(b"dir")),
       
  1318             VisitChildrenSet::Empty
       
  1319         );
       
  1320         assert_eq!(
       
  1321             m.visit_children_set(HgPath::new(b"dir/subdir")),
       
  1322             VisitChildrenSet::Empty
       
  1323         );
       
  1324 
       
  1325         // VisitchildrensetRootfilesin
       
  1326         let m = PatternMatcher::new(vec![IgnorePattern::new(
       
  1327             PatternSyntax::RootFiles,
       
  1328             b"dir/subdir",
       
  1329             Path::new(""),
       
  1330         )])
       
  1331         .unwrap();
       
  1332         assert_eq!(
       
  1333             m.visit_children_set(HgPath::new(b"dir/subdir/x")),
       
  1334             VisitChildrenSet::Empty
       
  1335         );
       
  1336         assert_eq!(
       
  1337             m.visit_children_set(HgPath::new(b"folder")),
       
  1338             VisitChildrenSet::Empty
       
  1339         );
       
  1340         // FIXME: These should probably be {'dir'}, {'subdir'} and This,
       
  1341         // respectively, or at least This for all three.
       
  1342         assert_eq!(
       
  1343             m.visit_children_set(HgPath::new(b"")),
       
  1344             VisitChildrenSet::Empty
       
  1345         );
       
  1346         assert_eq!(
       
  1347             m.visit_children_set(HgPath::new(b"dir")),
       
  1348             VisitChildrenSet::Empty
       
  1349         );
       
  1350         assert_eq!(
       
  1351             m.visit_children_set(HgPath::new(b"dir/subdir")),
       
  1352             VisitChildrenSet::Empty
       
  1353         );
       
  1354 
       
  1355         // VisitdirGlob
       
  1356         let m = PatternMatcher::new(vec![IgnorePattern::new(
       
  1357             PatternSyntax::Glob,
       
  1358             b"dir/z*",
       
  1359             Path::new(""),
       
  1360         )])
       
  1361         .unwrap();
       
  1362         assert_eq!(
       
  1363             m.visit_children_set(HgPath::new(b"")),
       
  1364             VisitChildrenSet::This
       
  1365         );
       
  1366         // FIXME: This probably should be This
       
  1367         assert_eq!(
       
  1368             m.visit_children_set(HgPath::new(b"dir")),
       
  1369             VisitChildrenSet::Empty
       
  1370         );
       
  1371         assert_eq!(
       
  1372             m.visit_children_set(HgPath::new(b"folder")),
       
  1373             VisitChildrenSet::Empty
       
  1374         );
       
  1375         // OPT: these should probably be False.
       
  1376         assert_eq!(
       
  1377             m.visit_children_set(HgPath::new(b"dir/subdir")),
       
  1378             VisitChildrenSet::This
       
  1379         );
       
  1380         assert_eq!(
       
  1381             m.visit_children_set(HgPath::new(b"dir/subdir/x")),
       
  1382             VisitChildrenSet::This
       
  1383         );
       
  1384 
       
  1385         // VisitchildrensetGlob
       
  1386         let m = PatternMatcher::new(vec![IgnorePattern::new(
       
  1387             PatternSyntax::Glob,
       
  1388             b"dir/z*",
       
  1389             Path::new(""),
       
  1390         )])
       
  1391         .unwrap();
       
  1392         assert_eq!(
       
  1393             m.visit_children_set(HgPath::new(b"")),
       
  1394             VisitChildrenSet::This
       
  1395         );
       
  1396         assert_eq!(
       
  1397             m.visit_children_set(HgPath::new(b"folder")),
       
  1398             VisitChildrenSet::Empty
       
  1399         );
       
  1400         // FIXME: This probably should be This
       
  1401         assert_eq!(
       
  1402             m.visit_children_set(HgPath::new(b"dir")),
       
  1403             VisitChildrenSet::Empty
       
  1404         );
       
  1405         // OPT: these should probably be Empty
       
  1406         assert_eq!(
       
  1407             m.visit_children_set(HgPath::new(b"dir/subdir")),
       
  1408             VisitChildrenSet::This
       
  1409         );
       
  1410         assert_eq!(
       
  1411             m.visit_children_set(HgPath::new(b"dir/subdir/x")),
       
  1412             VisitChildrenSet::This
       
  1413         );
       
  1414 
       
  1415         // VisitdirFilepath
       
  1416         let m = PatternMatcher::new(vec![IgnorePattern::new(
       
  1417             PatternSyntax::FilePath,
       
  1418             b"dir/z",
       
  1419             Path::new(""),
       
  1420         )])
       
  1421         .unwrap();
       
  1422         assert_eq!(
       
  1423             m.visit_children_set(HgPath::new(b"")),
       
  1424             VisitChildrenSet::This
       
  1425         );
       
  1426         assert_eq!(
       
  1427             m.visit_children_set(HgPath::new(b"dir")),
       
  1428             VisitChildrenSet::This
       
  1429         );
       
  1430         assert_eq!(
       
  1431             m.visit_children_set(HgPath::new(b"folder")),
       
  1432             VisitChildrenSet::Empty
       
  1433         );
       
  1434         assert_eq!(
       
  1435             m.visit_children_set(HgPath::new(b"dir/subdir")),
       
  1436             VisitChildrenSet::Empty
       
  1437         );
       
  1438         assert_eq!(
       
  1439             m.visit_children_set(HgPath::new(b"dir/subdir/x")),
       
  1440             VisitChildrenSet::Empty
       
  1441         );
       
  1442 
       
  1443         // VisitchildrensetFilepath
       
  1444         let m = PatternMatcher::new(vec![IgnorePattern::new(
       
  1445             PatternSyntax::FilePath,
       
  1446             b"dir/z",
       
  1447             Path::new(""),
       
  1448         )])
       
  1449         .unwrap();
       
  1450         assert_eq!(
       
  1451             m.visit_children_set(HgPath::new(b"")),
       
  1452             VisitChildrenSet::This
       
  1453         );
       
  1454         assert_eq!(
       
  1455             m.visit_children_set(HgPath::new(b"folder")),
       
  1456             VisitChildrenSet::Empty
       
  1457         );
       
  1458         assert_eq!(
       
  1459             m.visit_children_set(HgPath::new(b"dir")),
       
  1460             VisitChildrenSet::This
       
  1461         );
       
  1462         assert_eq!(
       
  1463             m.visit_children_set(HgPath::new(b"dir/subdir")),
       
  1464             VisitChildrenSet::Empty
       
  1465         );
       
  1466         assert_eq!(
       
  1467             m.visit_children_set(HgPath::new(b"dir/subdir/x")),
       
  1468             VisitChildrenSet::Empty
       
  1469         );
       
  1470     }
       
  1471 
       
  1472     #[test]
  1125     fn test_includematcher() {
  1473     fn test_includematcher() {
  1126         // VisitchildrensetPrefix
  1474         // VisitchildrensetPrefix
  1127         let matcher = IncludeMatcher::new(vec![IgnorePattern::new(
  1475         let matcher = IncludeMatcher::new(vec![IgnorePattern::new(
  1128             PatternSyntax::RelPath,
  1476             PatternSyntax::RelPath,
  1129             b"dir/subdir",
  1477             b"dir/subdir",