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", |