1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
//! Parse [path specifications](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefpathspecapathspec) and
//! see if a path matches.
#![deny(missing_docs, rust_2018_idioms)]
#![forbid(unsafe_code)]
use std::path::PathBuf;
use bitflags::bitflags;
use bstr::BString;
/// `gix-glob` types are available through [`attributes::glob`].
pub use gix_attributes as attributes;
///
pub mod normalize {
use std::path::PathBuf;
/// The error returned by [Pattern::normalize()](super::Pattern::normalize()).
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("The path '{}' is not inside of the worktree '{}'", path.display(), worktree_path.display())]
AbsolutePathOutsideOfWorktree { path: PathBuf, worktree_path: PathBuf },
#[error("The path '{}' leaves the repository", path.display())]
OutsideOfWorktree { path: PathBuf },
}
}
mod pattern;
///
pub mod search;
///
pub mod parse;
/// Default settings for some fields of a [`Pattern`].
///
/// These can be used to represent `GIT_*_PATHSPECS` environment variables, for example.
#[derive(Debug, Default, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct Defaults {
/// The default signature.
pub signature: MagicSignature,
/// The default search-mode.
///
/// Note that even if it's [`SearchMode::Literal`], the pathspecs will be parsed as usual, but matched verbatim afterwards.
///
/// Note that pathspecs can override this the [`SearchMode::Literal`] variant with an explicit `:(glob)` prefix.
pub search_mode: SearchMode,
/// If set, the pathspec will not be parsed but used verbatim. Implies [`SearchMode::Literal`] for `search_mode`.
pub literal: bool,
}
///
pub mod defaults;
/// A lists of pathspec patterns, possibly from a file.
///
/// Pathspecs are generally relative to the root of the repository.
#[derive(Debug, Clone)]
pub struct Search {
/// Patterns and their associated data in the order they were loaded in or specified,
/// the line number in its source file or its sequence number (_`(pattern, value, line_number)`_).
///
/// During matching, this order is reversed.
patterns: Vec<gix_glob::search::pattern::Mapping<search::Spec>>,
/// The path from which the patterns were read, or `None` if the patterns
/// don't originate in a file on disk.
pub source: Option<PathBuf>,
/// If `true`, this means all `patterns` are exclude patterns. This means that if there is no match
/// (which would exclude an item), we would actually match it for lack of exclusion.
all_patterns_are_excluded: bool,
/// The amount of bytes that are in common among all `patterns` and that aren't matched case-insensitively
common_prefix_len: usize,
}
/// The output of a pathspec [parsing][parse()] operation. It can be used to match against a one or more paths.
#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
pub struct Pattern {
/// The path part of a pathspec, which is typically a path possibly mixed with glob patterns.
/// Note that it might be an empty string as well.
///
/// For example, `:(top,literal,icase,attr,exclude)some/path` would yield `some/path`.
path: BString,
/// All magic signatures that were included in the pathspec.
pub signature: MagicSignature,
/// The search mode of the pathspec.
pub search_mode: SearchMode,
/// All attributes that were included in the `ATTR` part of the pathspec, if present.
///
/// `:(attr:a=one b=):path` would yield attribute `a` and `b`.
pub attributes: Vec<gix_attributes::Assignment>,
/// If `true`, we are a special Nil pattern and always match.
nil: bool,
/// The length of bytes in `path` that belong to the prefix, which will always be matched case-sensitively
/// on case-sensitive filesystems.
///
/// That way, even though pathspecs are applied from the top, we can emulate having changed directory into
/// a specific sub-directory in a case-sensitive file-system, even if the rest of the pathspec can be set to
/// match case-insensitively.
/// Is set by [Pattern::normalize()].
prefix_len: usize,
}
bitflags! {
/// Flags to represent 'magic signatures' which are parsed behind colons, like `:top:`.
#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
pub struct MagicSignature: u32 {
/// Matches patterns from the root of the repository
const TOP = 1 << 0;
/// Matches patterns in case insensitive mode
const ICASE = 1 << 1;
/// Excludes the matching patterns from the previous results
const EXCLUDE = 1 << 2;
/// The pattern must match a directory, and not a file.
/// This is equivalent to how it's handled in `gix-glob`
const MUST_BE_DIR = 1 << 3;
}
}
/// Parts of [magic signatures][MagicSignature] which don't stack as they all configure
/// the way path specs are matched.
#[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
pub enum SearchMode {
/// Expand special characters like `*` similar to how the shell would do it.
///
/// See [`PathAwareGlob`](SearchMode::PathAwareGlob) for the alternative.
#[default]
ShellGlob,
/// Special characters in the pattern, like `*` or `?`, are treated literally, effectively turning off globbing.
Literal,
/// A single `*` will not match a `/` in the pattern, but a `**` will
PathAwareGlob,
}
/// Parse a git-style pathspec into a [`Pattern`],
/// setting the given `default` values in case these aren't specified in `input`.
///
/// Note that empty [paths](Pattern::path) are allowed here, and generally some processing has to be performed.
pub fn parse(input: &[u8], default: Defaults) -> Result<Pattern, parse::Error> {
Pattern::from_bytes(input, default)
}