##// END OF EJS Templates
rust-filepatterns: add support for `include` and `subinclude` patterns...
Raphaël Gomès -
r44785:2fe89bec default
parent child Browse files
Show More
@@ -7,11 +7,19 b''
7
7
8 //! Handling of Mercurial-specific patterns.
8 //! Handling of Mercurial-specific patterns.
9
9
10 use crate::{utils::SliceExt, FastHashMap, PatternError};
10 use crate::{
11 utils::{
12 files::{canonical_path, get_bytes_from_path, get_path_from_bytes},
13 hg_path::{path_to_hg_path_buf, HgPathBuf, HgPathError},
14 SliceExt,
15 },
16 FastHashMap, PatternError,
17 };
11 use lazy_static::lazy_static;
18 use lazy_static::lazy_static;
12 use regex::bytes::{NoExpand, Regex};
19 use regex::bytes::{NoExpand, Regex};
13 use std::fs::File;
20 use std::fs::File;
14 use std::io::Read;
21 use std::io::Read;
22 use std::ops::Deref;
15 use std::path::{Path, PathBuf};
23 use std::path::{Path, PathBuf};
16 use std::vec::Vec;
24 use std::vec::Vec;
17
25
@@ -53,6 +61,10 b' pub enum PatternSyntax {'
53 /// A path relative to repository root, which is matched non-recursively
61 /// A path relative to repository root, which is matched non-recursively
54 /// (will not match subdirectories)
62 /// (will not match subdirectories)
55 RootFiles,
63 RootFiles,
64 /// A file of patterns to read and include
65 Include,
66 /// A file of patterns to match against files under the same directory
67 SubInclude,
56 }
68 }
57
69
58 /// Transforms a glob pattern into a regex
70 /// Transforms a glob pattern into a regex
@@ -145,6 +157,8 b' pub fn parse_pattern_syntax('
145 b"relre:" => Ok(PatternSyntax::RelRegexp),
157 b"relre:" => Ok(PatternSyntax::RelRegexp),
146 b"glob:" => Ok(PatternSyntax::Glob),
158 b"glob:" => Ok(PatternSyntax::Glob),
147 b"rootglob:" => Ok(PatternSyntax::RootGlob),
159 b"rootglob:" => Ok(PatternSyntax::RootGlob),
160 b"include:" => Ok(PatternSyntax::Include),
161 b"subinclude:" => Ok(PatternSyntax::SubInclude),
148 _ => Err(PatternError::UnsupportedSyntax(
162 _ => Err(PatternError::UnsupportedSyntax(
149 String::from_utf8_lossy(kind).to_string(),
163 String::from_utf8_lossy(kind).to_string(),
150 )),
164 )),
@@ -198,6 +212,7 b' fn _build_single_regex(entry: &IgnorePat'
198 PatternSyntax::Glob | PatternSyntax::RootGlob => {
212 PatternSyntax::Glob | PatternSyntax::RootGlob => {
199 [glob_to_re(pattern).as_slice(), GLOB_SUFFIX].concat()
213 [glob_to_re(pattern).as_slice(), GLOB_SUFFIX].concat()
200 }
214 }
215 PatternSyntax::Include | PatternSyntax::SubInclude => unreachable!(),
201 }
216 }
202 }
217 }
203
218
@@ -259,6 +274,9 b' pub fn build_single_regex('
259 | PatternSyntax::Path
274 | PatternSyntax::Path
260 | PatternSyntax::RelGlob
275 | PatternSyntax::RelGlob
261 | PatternSyntax::RootFiles => normalize_path_bytes(&pattern),
276 | PatternSyntax::RootFiles => normalize_path_bytes(&pattern),
277 PatternSyntax::Include | PatternSyntax::SubInclude => {
278 return Err(PatternError::NonRegexPattern(entry.clone()))
279 }
262 _ => pattern.to_owned(),
280 _ => pattern.to_owned(),
263 };
281 };
264 if *syntax == PatternSyntax::RootGlob
282 if *syntax == PatternSyntax::RootGlob
@@ -422,6 +440,110 b' impl IgnorePattern {'
422
440
423 pub type PatternResult<T> = Result<T, PatternError>;
441 pub type PatternResult<T> = Result<T, PatternError>;
424
442
443 /// Wrapper for `read_pattern_file` that also recursively expands `include:`
444 /// patterns.
445 ///
446 /// `subinclude:` is not treated as a special pattern here: unraveling them
447 /// needs to occur in the "ignore" phase.
448 pub fn get_patterns_from_file(
449 pattern_file: impl AsRef<Path>,
450 root_dir: impl AsRef<Path>,
451 ) -> PatternResult<(Vec<IgnorePattern>, Vec<PatternFileWarning>)> {
452 let (patterns, mut warnings) = read_pattern_file(&pattern_file, true)?;
453 let patterns = patterns
454 .into_iter()
455 .flat_map(|entry| -> PatternResult<_> {
456 let IgnorePattern {
457 syntax,
458 pattern,
459 source: _,
460 } = &entry;
461 Ok(match syntax {
462 PatternSyntax::Include => {
463 let inner_include =
464 root_dir.as_ref().join(get_path_from_bytes(&pattern));
465 let (inner_pats, inner_warnings) = get_patterns_from_file(
466 &inner_include,
467 root_dir.as_ref(),
468 )?;
469 warnings.extend(inner_warnings);
470 inner_pats
471 }
472 _ => vec![entry],
473 })
474 })
475 .flatten()
476 .collect();
477
478 Ok((patterns, warnings))
479 }
480
481 /// Holds all the information needed to handle a `subinclude:` pattern.
482 pub struct SubInclude {
483 /// Will be used for repository (hg) paths that start with this prefix.
484 /// It is relative to the current working directory, so comparing against
485 /// repository paths is painless.
486 pub prefix: HgPathBuf,
487 /// The file itself, containing the patterns
488 pub path: PathBuf,
489 /// Folder in the filesystem where this it applies
490 pub root: PathBuf,
491 }
492
493 impl SubInclude {
494 pub fn new(
495 root_dir: impl AsRef<Path>,
496 pattern: &[u8],
497 source: impl AsRef<Path>,
498 ) -> Result<SubInclude, HgPathError> {
499 let normalized_source =
500 normalize_path_bytes(&get_bytes_from_path(source));
501
502 let source_root = get_path_from_bytes(&normalized_source);
503 let source_root = source_root.parent().unwrap_or(source_root.deref());
504
505 let path = source_root.join(get_path_from_bytes(pattern));
506 let new_root = path.parent().unwrap_or(path.deref());
507
508 let prefix = canonical_path(&root_dir, &root_dir, new_root)?;
509
510 Ok(Self {
511 prefix: path_to_hg_path_buf(prefix).and_then(|mut p| {
512 if !p.is_empty() {
513 p.push(b'/');
514 }
515 Ok(p)
516 })?,
517 path: path.to_owned(),
518 root: new_root.to_owned(),
519 })
520 }
521 }
522
523 /// Separate and pre-process subincludes from other patterns for the "ignore"
524 /// phase.
525 pub fn filter_subincludes(
526 ignore_patterns: &[IgnorePattern],
527 root_dir: impl AsRef<Path>,
528 ) -> Result<(Vec<SubInclude>, Vec<&IgnorePattern>), HgPathError> {
529 let mut subincludes = vec![];
530 let mut others = vec![];
531
532 for ignore_pattern in ignore_patterns.iter() {
533 let IgnorePattern {
534 syntax,
535 pattern,
536 source,
537 } = ignore_pattern;
538 if *syntax == PatternSyntax::SubInclude {
539 subincludes.push(SubInclude::new(&root_dir, pattern, &source)?);
540 } else {
541 others.push(ignore_pattern)
542 }
543 }
544 Ok((subincludes, others))
545 }
546
425 #[cfg(test)]
547 #[cfg(test)]
426 mod tests {
548 mod tests {
427 use super::*;
549 use super::*;
@@ -121,6 +121,9 b' pub enum PatternError {'
121 UnsupportedSyntaxInFile(String, String, usize),
121 UnsupportedSyntaxInFile(String, String, usize),
122 TooLong(usize),
122 TooLong(usize),
123 IO(std::io::Error),
123 IO(std::io::Error),
124 /// Needed a pattern that can be turned into a regex but got one that
125 /// can't. This should only happen through programmer error.
126 NonRegexPattern(IgnorePattern),
124 }
127 }
125
128
126 impl ToString for PatternError {
129 impl ToString for PatternError {
@@ -140,6 +143,9 b' impl ToString for PatternError {'
140 }
143 }
141 PatternError::IO(e) => e.to_string(),
144 PatternError::IO(e) => e.to_string(),
142 PatternError::Path(e) => e.to_string(),
145 PatternError::Path(e) => e.to_string(),
146 PatternError::NonRegexPattern(pattern) => {
147 format!("'{:?}' cannot be turned into a regex", pattern)
148 }
143 }
149 }
144 }
150 }
145 }
151 }
General Comments 0
You need to be logged in to leave comments. Login now