##// END OF EJS Templates
rust-matchers: add PatternMatcher...
Spencer Baugh -
r51758:f874342f default
parent child Browse files
Show More
@@ -250,6 +250,118 b' impl Matcher for FileMatcher {'
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::{
@@ -1122,6 +1234,242 b' mod tests {'
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(
General Comments 0
You need to be logged in to leave comments. Login now