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