diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
commit | 9835e2ae736235810b4ea1c162ca5e65c547e770 (patch) | |
tree | 3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/gix-ignore/src/search.rs | |
parent | Releasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff) | |
download | rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip |
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-ignore/src/search.rs')
-rw-r--r-- | vendor/gix-ignore/src/search.rs | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/vendor/gix-ignore/src/search.rs b/vendor/gix-ignore/src/search.rs new file mode 100644 index 000000000..5c957f136 --- /dev/null +++ b/vendor/gix-ignore/src/search.rs @@ -0,0 +1,183 @@ +use std::{ + ffi::OsString, + path::{Path, PathBuf}, +}; + +use bstr::{BStr, ByteSlice}; +use gix_glob::search::{pattern, Pattern}; + +use crate::Search; + +/// Describes a matching pattern within a search for ignored paths. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] +pub struct Match<'a, T> { + /// The glob pattern itself, like `/target/*`. + pub pattern: &'a gix_glob::Pattern, + /// The value associated with the pattern. + pub value: &'a T, + /// The path to the source from which the pattern was loaded, or `None` if it was specified by other means. + pub source: Option<&'a Path>, + /// The line at which the pattern was found in its `source` file, or the occurrence in which it was provided. + pub sequence_number: usize, +} + +/// An implementation of the [`Pattern`] trait for ignore patterns. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Default)] +pub struct Ignore; + +impl Pattern for Ignore { + type Value = (); + + fn bytes_to_patterns(bytes: &[u8], _source: &std::path::Path) -> Vec<pattern::Mapping<Self::Value>> { + crate::parse(bytes) + .map(|(pattern, line_number)| pattern::Mapping { + pattern, + value: (), + sequence_number: line_number, + }) + .collect() + } + + fn may_use_glob_pattern(_pattern: &gix_glob::Pattern) -> bool { + true + } +} + +/// Instantiation of a search for ignore patterns. +impl Search { + /// Given `git_dir`, a `.git` repository, load static ignore patterns from `info/exclude` + /// and from `excludes_file` if it is provided. + /// Note that it's not considered an error if the provided `excludes_file` does not exist. + pub fn from_git_dir( + git_dir: impl AsRef<Path>, + excludes_file: Option<PathBuf>, + buf: &mut Vec<u8>, + ) -> std::io::Result<Self> { + let mut group = Self::default(); + + let follow_symlinks = true; + // order matters! More important ones first. + group.patterns.extend( + excludes_file + .and_then(|file| pattern::List::<Ignore>::from_file(file, None, follow_symlinks, buf).transpose()) + .transpose()?, + ); + group.patterns.extend(pattern::List::<Ignore>::from_file( + git_dir.as_ref().join("info").join("exclude"), + None, + follow_symlinks, + buf, + )?); + Ok(group) + } + + /// Parse a list of patterns, using slashes as path separators + pub fn from_overrides(patterns: impl IntoIterator<Item = impl Into<OsString>>) -> Self { + Search { + patterns: vec![pattern::List { + patterns: patterns + .into_iter() + .map(Into::into) + .enumerate() + .filter_map(|(seq_id, pattern)| { + let pattern = gix_path::try_into_bstr(PathBuf::from(pattern)).ok()?; + gix_glob::parse(pattern.as_ref()).map(|p| pattern::Mapping { + pattern: p, + value: (), + sequence_number: seq_id, + }) + }) + .collect(), + source: None, + base: None, + }], + } + } +} + +/// Mutation +impl Search { + /// Add patterns as parsed from `bytes`, providing their `source` path and possibly their `root` path, the path they + /// are relative to. This also means that `source` is contained within `root` if `root` is provided. + pub fn add_patterns_buffer(&mut self, bytes: &[u8], source: impl Into<PathBuf>, root: Option<&Path>) { + self.patterns + .push(pattern::List::from_bytes(bytes, source.into(), root)); + } +} + +/// Return a match if a pattern matches `relative_path`, providing a pre-computed `basename_pos` which is the +/// starting position of the basename of `relative_path`. `is_dir` is true if `relative_path` is a directory. +/// `case` specifies whether cases should be folded during matching or not. +pub fn pattern_matching_relative_path<'a>( + list: &'a gix_glob::search::pattern::List<Ignore>, + relative_path: &BStr, + basename_pos: Option<usize>, + is_dir: Option<bool>, + case: gix_glob::pattern::Case, +) -> Option<Match<'a, ()>> { + let (relative_path, basename_start_pos) = + list.strip_base_handle_recompute_basename_pos(relative_path, basename_pos, case)?; + list.patterns + .iter() + .rev() + .filter(|pm| Ignore::may_use_glob_pattern(&pm.pattern)) + .find_map( + |pattern::Mapping { + pattern, + value, + sequence_number, + }| { + pattern + .matches_repo_relative_path(relative_path, basename_start_pos, is_dir, case) + .then_some(Match { + pattern, + value, + source: list.source.as_deref(), + sequence_number: *sequence_number, + }) + }, + ) +} + +/// Like [`pattern_matching_relative_path()`], but returns an index to the pattern +/// that matched `relative_path`, instead of the match itself. +pub fn pattern_idx_matching_relative_path( + list: &gix_glob::search::pattern::List<Ignore>, + relative_path: &BStr, + basename_pos: Option<usize>, + is_dir: Option<bool>, + case: gix_glob::pattern::Case, +) -> Option<usize> { + let (relative_path, basename_start_pos) = + list.strip_base_handle_recompute_basename_pos(relative_path, basename_pos, case)?; + list.patterns + .iter() + .enumerate() + .rev() + .filter(|(_, pm)| Ignore::may_use_glob_pattern(&pm.pattern)) + .find_map(|(idx, pm)| { + pm.pattern + .matches_repo_relative_path(relative_path, basename_start_pos, is_dir, case) + .then_some(idx) + }) +} + +/// Matching of ignore patterns. +impl Search { + /// Match `relative_path` and return the first match if found. + /// `is_dir` is true if `relative_path` is a directory. + /// `case` specifies whether cases should be folded during matching or not. + pub fn pattern_matching_relative_path<'a>( + &self, + relative_path: impl Into<&'a BStr>, + is_dir: Option<bool>, + case: gix_glob::pattern::Case, + ) -> Option<Match<'_, ()>> { + let relative_path = relative_path.into(); + let basename_pos = relative_path.rfind(b"/").map(|p| p + 1); + self.patterns + .iter() + .rev() + .find_map(|pl| pattern_matching_relative_path(pl, relative_path, basename_pos, is_dir, case)) + } +} |