From patchwork Wed Mar 11 16:23:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: D7923: rust-matchers: add functions to get roots, dirs and parents from patterns From: phabricator X-Patchwork-Id: 45716 Message-Id: <5d4e783f18ce997b4d4f9866d88fe402@localhost.localdomain> To: Phabricator Cc: mercurial-devel@mercurial-scm.org Date: Wed, 11 Mar 2020 16:23:05 +0000 Closed by commit rHGd4e8cfcde012: rust-matchers: add functions to get roots, dirs and parents from patterns (authored by Alphare). This revision was automatically updated to reflect the committed changes. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D7923?vs=20161&id=20713 CHANGES SINCE LAST ACTION https://phab.mercurial-scm.org/D7923/new/ REVISION DETAIL https://phab.mercurial-scm.org/D7923 AFFECTED FILES rust/hg-core/src/matchers.rs CHANGE DETAILS To: Alphare, #hg-reviewers, kevincox, martinvonz Cc: durin42, kevincox, mercurial-devel diff --git a/rust/hg-core/src/matchers.rs b/rust/hg-core/src/matchers.rs --- a/rust/hg-core/src/matchers.rs +++ b/rust/hg-core/src/matchers.rs @@ -10,8 +10,10 @@ #[cfg(feature = "with-re2")] use crate::re2::Re2; use crate::{ - filepatterns::PatternResult, utils::hg_path::HgPath, DirsMultiset, - DirstateMapError, PatternError, + filepatterns::PatternResult, + utils::hg_path::{HgPath, HgPathBuf}, + DirsMultiset, DirstateMapError, IgnorePattern, PatternError, + PatternSyntax, }; use std::collections::HashSet; use std::iter::FromIterator; @@ -240,10 +242,156 @@ Err(PatternError::Re2NotInstalled) } +/// Returns roots and directories corresponding to each pattern. +/// +/// This calculates the roots and directories exactly matching the patterns and +/// returns a tuple of (roots, dirs). It does not return other directories +/// which may also need to be considered, like the parent directories. +fn roots_and_dirs( + ignore_patterns: &[IgnorePattern], +) -> (Vec, Vec) { + let mut roots = Vec::new(); + let mut dirs = Vec::new(); + + for ignore_pattern in ignore_patterns { + let IgnorePattern { + syntax, pattern, .. + } = ignore_pattern; + match syntax { + PatternSyntax::RootGlob | PatternSyntax::Glob => { + let mut root = vec![]; + + for p in pattern.split(|c| *c == b'/') { + if p.iter().any(|c| match *c { + b'[' | b'{' | b'*' | b'?' => true, + _ => false, + }) { + break; + } + root.push(HgPathBuf::from_bytes(p)); + } + let buf = + root.iter().fold(HgPathBuf::new(), |acc, r| acc.join(r)); + roots.push(buf); + } + PatternSyntax::Path | PatternSyntax::RelPath => { + let pat = HgPath::new(if pattern == b"." { + &[] as &[u8] + } else { + pattern + }); + roots.push(pat.to_owned()); + } + PatternSyntax::RootFiles => { + let pat = if pattern == b"." { + &[] as &[u8] + } else { + pattern + }; + dirs.push(HgPathBuf::from_bytes(pat)); + } + _ => { + roots.push(HgPathBuf::new()); + } + } + } + (roots, dirs) +} + +/// Paths extracted from patterns +#[derive(Debug, PartialEq)] +struct RootsDirsAndParents { + /// Directories to match recursively + pub roots: HashSet, + /// Directories to match non-recursively + pub dirs: HashSet, + /// Implicitly required directories to go to items in either roots or dirs + pub parents: HashSet, +} + +/// Extract roots, dirs and parents from patterns. +fn roots_dirs_and_parents( + ignore_patterns: &[IgnorePattern], +) -> PatternResult { + let (roots, dirs) = roots_and_dirs(ignore_patterns); + + let mut parents = HashSet::new(); + + parents.extend( + DirsMultiset::from_manifest(&dirs) + .map_err(|e| match e { + DirstateMapError::InvalidPath(e) => e, + _ => unreachable!(), + })? + .iter() + .map(|k| k.to_owned()), + ); + parents.extend( + DirsMultiset::from_manifest(&roots) + .map_err(|e| match e { + DirstateMapError::InvalidPath(e) => e, + _ => unreachable!(), + })? + .iter() + .map(|k| k.to_owned()), + ); + + Ok(RootsDirsAndParents { + roots: HashSet::from_iter(roots), + dirs: HashSet::from_iter(dirs), + parents, + }) +} + #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; + use std::path::Path; + + #[test] + fn test_roots_and_dirs() { + let pats = vec![ + IgnorePattern::new(PatternSyntax::Glob, b"g/h/*", Path::new("")), + IgnorePattern::new(PatternSyntax::Glob, b"g/h", Path::new("")), + IgnorePattern::new(PatternSyntax::Glob, b"g*", Path::new("")), + ]; + let (roots, dirs) = roots_and_dirs(&pats); + + assert_eq!( + roots, + vec!( + HgPathBuf::from_bytes(b"g/h"), + HgPathBuf::from_bytes(b"g/h"), + HgPathBuf::new() + ), + ); + assert_eq!(dirs, vec!()); + } + + #[test] + fn test_roots_dirs_and_parents() { + let pats = vec![ + IgnorePattern::new(PatternSyntax::Glob, b"g/h/*", Path::new("")), + IgnorePattern::new(PatternSyntax::Glob, b"g/h", Path::new("")), + IgnorePattern::new(PatternSyntax::Glob, b"g*", Path::new("")), + ]; + + let mut roots = HashSet::new(); + roots.insert(HgPathBuf::from_bytes(b"g/h")); + roots.insert(HgPathBuf::new()); + + let dirs = HashSet::new(); + + let mut parents = HashSet::new(); + parents.insert(HgPathBuf::new()); + parents.insert(HgPathBuf::from_bytes(b"g")); + + assert_eq!( + roots_dirs_and_parents(&pats).unwrap(), + RootsDirsAndParents {roots, dirs, parents} + ); + } #[test] fn test_filematcher_visit_children_set() {