mirror of
				https://github.com/BurntSushi/ripgrep.git
				synced 2025-10-30 23:17:47 +02:00 
			
		
		
		
	
		
			
	
	
		
			759 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
		
		
			
		
	
	
			759 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
|   | /*!
 | ||
|  | Provides the definition of low level arguments from CLI flags.
 | ||
|  | */
 | ||
|  | 
 | ||
|  | use std::{
 | ||
|  |     ffi::{OsStr, OsString},
 | ||
|  |     path::PathBuf,
 | ||
|  | };
 | ||
|  | 
 | ||
|  | use {
 | ||
|  |     bstr::{BString, ByteVec},
 | ||
|  |     grep::printer::{HyperlinkFormat, UserColorSpec},
 | ||
|  | };
 | ||
|  | 
 | ||
|  | /// A collection of "low level" arguments.
 | ||
|  | ///
 | ||
|  | /// The "low level" here is meant to constrain this type to be as close to the
 | ||
|  | /// actual CLI flags and arguments as possible. Namely, other than some
 | ||
|  | /// convenience types to help validate flag values and deal with overrides
 | ||
|  | /// between flags, these low level arguments do not contain any higher level
 | ||
|  | /// abstractions.
 | ||
|  | ///
 | ||
|  | /// Another self-imposed constraint is that populating low level arguments
 | ||
|  | /// should not require anything other than validating what the user has
 | ||
|  | /// provided. For example, low level arguments should not contain a
 | ||
|  | /// `HyperlinkConfig`, since in order to get a full configuration, one needs to
 | ||
|  | /// discover the hostname of the current system (which might require running a
 | ||
|  | /// binary or a syscall).
 | ||
|  | ///
 | ||
|  | /// Low level arguments are populated by the parser directly via the `update`
 | ||
|  | /// method on the corresponding implementation of the `Flag` trait.
 | ||
|  | #[derive(Debug, Default)]
 | ||
|  | pub(crate) struct LowArgs {
 | ||
|  |     // Essential arguments.
 | ||
|  |     pub(crate) special: Option<SpecialMode>,
 | ||
|  |     pub(crate) mode: Mode,
 | ||
|  |     pub(crate) positional: Vec<OsString>,
 | ||
|  |     pub(crate) patterns: Vec<PatternSource>,
 | ||
|  |     // Everything else, sorted lexicographically.
 | ||
|  |     pub(crate) binary: BinaryMode,
 | ||
|  |     pub(crate) boundary: Option<BoundaryMode>,
 | ||
|  |     pub(crate) buffer: BufferMode,
 | ||
|  |     pub(crate) byte_offset: bool,
 | ||
|  |     pub(crate) case: CaseMode,
 | ||
|  |     pub(crate) color: ColorChoice,
 | ||
|  |     pub(crate) colors: Vec<UserColorSpec>,
 | ||
|  |     pub(crate) column: Option<bool>,
 | ||
|  |     pub(crate) context: ContextMode,
 | ||
|  |     pub(crate) context_separator: ContextSeparator,
 | ||
|  |     pub(crate) crlf: bool,
 | ||
|  |     pub(crate) dfa_size_limit: Option<usize>,
 | ||
|  |     pub(crate) encoding: EncodingMode,
 | ||
|  |     pub(crate) engine: EngineChoice,
 | ||
|  |     pub(crate) field_context_separator: FieldContextSeparator,
 | ||
|  |     pub(crate) field_match_separator: FieldMatchSeparator,
 | ||
|  |     pub(crate) fixed_strings: bool,
 | ||
|  |     pub(crate) follow: bool,
 | ||
|  |     pub(crate) glob_case_insensitive: bool,
 | ||
|  |     pub(crate) globs: Vec<String>,
 | ||
|  |     pub(crate) heading: Option<bool>,
 | ||
|  |     pub(crate) hidden: bool,
 | ||
|  |     pub(crate) hostname_bin: Option<PathBuf>,
 | ||
|  |     pub(crate) hyperlink_format: HyperlinkFormat,
 | ||
|  |     pub(crate) iglobs: Vec<String>,
 | ||
|  |     pub(crate) ignore_file: Vec<PathBuf>,
 | ||
|  |     pub(crate) ignore_file_case_insensitive: bool,
 | ||
|  |     pub(crate) include_zero: bool,
 | ||
|  |     pub(crate) invert_match: bool,
 | ||
|  |     pub(crate) line_number: Option<bool>,
 | ||
|  |     pub(crate) logging: Option<LoggingMode>,
 | ||
|  |     pub(crate) max_columns: Option<u64>,
 | ||
|  |     pub(crate) max_columns_preview: bool,
 | ||
|  |     pub(crate) max_count: Option<u64>,
 | ||
|  |     pub(crate) max_depth: Option<usize>,
 | ||
|  |     pub(crate) max_filesize: Option<u64>,
 | ||
|  |     pub(crate) mmap: MmapMode,
 | ||
|  |     pub(crate) multiline: bool,
 | ||
|  |     pub(crate) multiline_dotall: bool,
 | ||
|  |     pub(crate) no_config: bool,
 | ||
|  |     pub(crate) no_ignore_dot: bool,
 | ||
|  |     pub(crate) no_ignore_exclude: bool,
 | ||
|  |     pub(crate) no_ignore_files: bool,
 | ||
|  |     pub(crate) no_ignore_global: bool,
 | ||
|  |     pub(crate) no_ignore_messages: bool,
 | ||
|  |     pub(crate) no_ignore_parent: bool,
 | ||
|  |     pub(crate) no_ignore_vcs: bool,
 | ||
|  |     pub(crate) no_messages: bool,
 | ||
|  |     pub(crate) no_require_git: bool,
 | ||
|  |     pub(crate) no_unicode: bool,
 | ||
|  |     pub(crate) null: bool,
 | ||
|  |     pub(crate) null_data: bool,
 | ||
|  |     pub(crate) one_file_system: bool,
 | ||
|  |     pub(crate) only_matching: bool,
 | ||
|  |     pub(crate) path_separator: Option<u8>,
 | ||
|  |     pub(crate) pre: Option<PathBuf>,
 | ||
|  |     pub(crate) pre_glob: Vec<String>,
 | ||
|  |     pub(crate) quiet: bool,
 | ||
|  |     pub(crate) regex_size_limit: Option<usize>,
 | ||
|  |     pub(crate) replace: Option<BString>,
 | ||
|  |     pub(crate) search_zip: bool,
 | ||
|  |     pub(crate) sort: Option<SortMode>,
 | ||
|  |     pub(crate) stats: bool,
 | ||
|  |     pub(crate) stop_on_nonmatch: bool,
 | ||
|  |     pub(crate) threads: Option<usize>,
 | ||
|  |     pub(crate) trim: bool,
 | ||
|  |     pub(crate) type_changes: Vec<TypeChange>,
 | ||
|  |     pub(crate) unrestricted: usize,
 | ||
|  |     pub(crate) vimgrep: bool,
 | ||
|  |     pub(crate) with_filename: Option<bool>,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// A "special" mode that supercedes everything else.
 | ||
|  | ///
 | ||
|  | /// When one of these modes is present, it overrides everything else and causes
 | ||
|  | /// ripgrep to short-circuit. In particular, we avoid converting low-level
 | ||
|  | /// argument types into higher level arguments types that can fail for various
 | ||
|  | /// reasons related to the environment. (Parsing the low-level arguments can
 | ||
|  | /// fail too, but usually not in a way that can't be worked around by removing
 | ||
|  | /// the corresponding arguments from the CLI command.) This is overall a hedge
 | ||
|  | /// to ensure that version and help information are basically always available.
 | ||
|  | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum SpecialMode {
 | ||
|  |     /// Show a condensed version of "help" output. Generally speaking, this
 | ||
|  |     /// shows each flag and an extremely terse description of that flag on
 | ||
|  |     /// a single line. This corresponds to the `-h` flag.
 | ||
|  |     HelpShort,
 | ||
|  |     /// Shows a very verbose version of the "help" output. The docs for some
 | ||
|  |     /// flags will be paragraphs long. This corresponds to the `--help` flag.
 | ||
|  |     HelpLong,
 | ||
|  |     /// Show condensed version information. e.g., `ripgrep x.y.z`.
 | ||
|  |     VersionShort,
 | ||
|  |     /// Show verbose version information. Includes "short" information as well
 | ||
|  |     /// as features included in the build.
 | ||
|  |     VersionLong,
 | ||
|  |     /// Show PCRE2's version information, or an error if this version of
 | ||
|  |     /// ripgrep wasn't compiled with PCRE2 support.
 | ||
|  |     VersionPCRE2,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// The overall mode that ripgrep should operate in.
 | ||
|  | ///
 | ||
|  | /// If ripgrep were designed without the legacy of grep, these would probably
 | ||
|  | /// be sub-commands? Perhaps not, since they aren't as frequently used.
 | ||
|  | ///
 | ||
|  | /// The point of putting these in one enum is that they are all mutually
 | ||
|  | /// exclusive and override one another.
 | ||
|  | ///
 | ||
|  | /// Note that -h/--help and -V/--version are not included in this because
 | ||
|  | /// they always overrides everything else, regardless of where it appears
 | ||
|  | /// in the command line. They are treated as "special" modes that short-circuit
 | ||
|  | /// ripgrep's usual flow.
 | ||
|  | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum Mode {
 | ||
|  |     /// ripgrep will execute a search of some kind.
 | ||
|  |     Search(SearchMode),
 | ||
|  |     /// Show the files that *would* be searched, but don't actually search
 | ||
|  |     /// them.
 | ||
|  |     Files,
 | ||
|  |     /// List all file type definitions configured, including the default file
 | ||
|  |     /// types and any additional file types added to the command line.
 | ||
|  |     Types,
 | ||
|  |     /// Generate various things like the man page and completion files.
 | ||
|  |     Generate(GenerateMode),
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl Default for Mode {
 | ||
|  |     fn default() -> Mode {
 | ||
|  |         Mode::Search(SearchMode::Standard)
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl Mode {
 | ||
|  |     /// Update this mode to the new mode while implementing various override
 | ||
|  |     /// semantics. For example, a search mode cannot override a non-search
 | ||
|  |     /// mode.
 | ||
|  |     pub(crate) fn update(&mut self, new: Mode) {
 | ||
|  |         match *self {
 | ||
|  |             // If we're in a search mode, then anything can override it.
 | ||
|  |             Mode::Search(_) => *self = new,
 | ||
|  |             _ => {
 | ||
|  |                 // Once we're in a non-search mode, other non-search modes
 | ||
|  |                 // can override it. But search modes cannot. So for example,
 | ||
|  |                 // `--files -l` will still be Mode::Files.
 | ||
|  |                 if !matches!(*self, Mode::Search(_)) {
 | ||
|  |                     *self = new;
 | ||
|  |                 }
 | ||
|  |             }
 | ||
|  |         }
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// The kind of search that ripgrep is going to perform.
 | ||
|  | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum SearchMode {
 | ||
|  |     /// The default standard mode of operation. ripgrep looks for matches and
 | ||
|  |     /// prints them when found.
 | ||
|  |     ///
 | ||
|  |     /// There is no specific flag for this mode since it's the default. But
 | ||
|  |     /// some of the modes below, like JSON, have negation flags like --no-json
 | ||
|  |     /// that let you revert back to this default mode.
 | ||
|  |     Standard,
 | ||
|  |     /// Show files containing at least one match.
 | ||
|  |     FilesWithMatches,
 | ||
|  |     /// Show files that don't contain any matches.
 | ||
|  |     FilesWithoutMatch,
 | ||
|  |     /// Show files containing at least one match and the number of matching
 | ||
|  |     /// lines.
 | ||
|  |     Count,
 | ||
|  |     /// Show files containing at least one match and the total number of
 | ||
|  |     /// matches.
 | ||
|  |     CountMatches,
 | ||
|  |     /// Print matches in a JSON lines format.
 | ||
|  |     JSON,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// The thing to generate via the --generate flag.
 | ||
|  | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum GenerateMode {
 | ||
|  |     /// Generate the raw roff used for the man page.
 | ||
|  |     Man,
 | ||
|  |     /// Completions for bash.
 | ||
|  |     CompleteBash,
 | ||
|  |     /// Completions for zsh.
 | ||
|  |     CompleteZsh,
 | ||
|  |     /// Completions for fish.
 | ||
|  |     CompleteFish,
 | ||
|  |     /// Completions for PowerShell.
 | ||
|  |     CompletePowerShell,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// Indicates how ripgrep should treat binary data.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum BinaryMode {
 | ||
|  |     /// Automatically determine the binary mode to use. Essentially, when
 | ||
|  |     /// a file is searched explicitly, then it will be searched using the
 | ||
|  |     /// `SearchAndSuppress` strategy. Otherwise, it will be searched in a way
 | ||
|  |     /// that attempts to skip binary files as much as possible. That is, once
 | ||
|  |     /// a file is classified as binary, searching will immediately stop.
 | ||
|  |     Auto,
 | ||
|  |     /// Search files even when they have binary data, but if a match is found,
 | ||
|  |     /// suppress it and emit a warning.
 | ||
|  |     ///
 | ||
|  |     /// In this mode, `NUL` bytes are replaced with line terminators. This is
 | ||
|  |     /// a heuristic meant to reduce heap memory usage, since true binary data
 | ||
|  |     /// isn't line oriented. If one attempts to treat such data as line
 | ||
|  |     /// oriented, then one may wind up with impractically large lines. For
 | ||
|  |     /// example, many binary files contain very long runs of NUL bytes.
 | ||
|  |     SearchAndSuppress,
 | ||
|  |     /// Treat all files as if they were plain text. There's no skipping and no
 | ||
|  |     /// replacement of `NUL` bytes with line terminators.
 | ||
|  |     AsText,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl Default for BinaryMode {
 | ||
|  |     fn default() -> BinaryMode {
 | ||
|  |         BinaryMode::Auto
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// Indicates what kind of boundary mode to use (line or word).
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum BoundaryMode {
 | ||
|  |     /// Only allow matches when surrounded by line bounaries.
 | ||
|  |     Line,
 | ||
|  |     /// Only allow matches when surrounded by word bounaries.
 | ||
|  |     Word,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// Indicates the buffer mode that ripgrep should use when printing output.
 | ||
|  | ///
 | ||
|  | /// The default is `Auto`.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum BufferMode {
 | ||
|  |     /// Select the buffer mode, 'line' or 'block', automatically based on
 | ||
|  |     /// whether stdout is connected to a tty.
 | ||
|  |     Auto,
 | ||
|  |     /// Flush the output buffer whenever a line terminator is seen.
 | ||
|  |     ///
 | ||
|  |     /// This is useful when wants to see search results more immediately,
 | ||
|  |     /// for example, with `tail -f`.
 | ||
|  |     Line,
 | ||
|  |     /// Flush the output buffer whenever it reaches some fixed size. The size
 | ||
|  |     /// is usually big enough to hold many lines.
 | ||
|  |     ///
 | ||
|  |     /// This is useful for maximum performance, particularly when printing
 | ||
|  |     /// lots of results.
 | ||
|  |     Block,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl Default for BufferMode {
 | ||
|  |     fn default() -> BufferMode {
 | ||
|  |         BufferMode::Auto
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// Indicates the case mode for how to interpret all patterns given to ripgrep.
 | ||
|  | ///
 | ||
|  | /// The default is `Sensitive`.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum CaseMode {
 | ||
|  |     /// Patterns are matched case sensitively. i.e., `a` does not match `A`.
 | ||
|  |     Sensitive,
 | ||
|  |     /// Patterns are matched case insensitively. i.e., `a` does match `A`.
 | ||
|  |     Insensitive,
 | ||
|  |     /// Patterns are automatically matched case insensitively only when they
 | ||
|  |     /// consist of all lowercase literal characters. For example, the pattern
 | ||
|  |     /// `a` will match `A` but `A` will not match `a`.
 | ||
|  |     Smart,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl Default for CaseMode {
 | ||
|  |     fn default() -> CaseMode {
 | ||
|  |         CaseMode::Sensitive
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// Indicates whether ripgrep should include color/hyperlinks in its output.
 | ||
|  | ///
 | ||
|  | /// The default is `Auto`.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum ColorChoice {
 | ||
|  |     /// Color and hyperlinks will never be used.
 | ||
|  |     Never,
 | ||
|  |     /// Color and hyperlinks will be used only when stdout is connected to a
 | ||
|  |     /// tty.
 | ||
|  |     Auto,
 | ||
|  |     /// Color will always be used.
 | ||
|  |     Always,
 | ||
|  |     /// Color will always be used and only ANSI escapes will be used.
 | ||
|  |     ///
 | ||
|  |     /// This only makes sense in the context of legacy Windows console APIs.
 | ||
|  |     /// At time of writing, ripgrep will try to use the legacy console APIs
 | ||
|  |     /// if ANSI coloring isn't believed to be possible. This option will force
 | ||
|  |     /// ripgrep to use ANSI coloring.
 | ||
|  |     Ansi,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl Default for ColorChoice {
 | ||
|  |     fn default() -> ColorChoice {
 | ||
|  |         ColorChoice::Auto
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl ColorChoice {
 | ||
|  |     /// Convert this color choice to the corresponding termcolor type.
 | ||
|  |     pub(crate) fn to_termcolor(&self) -> termcolor::ColorChoice {
 | ||
|  |         match *self {
 | ||
|  |             ColorChoice::Never => termcolor::ColorChoice::Never,
 | ||
|  |             ColorChoice::Auto => termcolor::ColorChoice::Auto,
 | ||
|  |             ColorChoice::Always => termcolor::ColorChoice::Always,
 | ||
|  |             ColorChoice::Ansi => termcolor::ColorChoice::AlwaysAnsi,
 | ||
|  |         }
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// Indicates the line context options ripgrep should use for output.
 | ||
|  | ///
 | ||
|  | /// The default is no context at all.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum ContextMode {
 | ||
|  |     /// All lines will be printed. That is, the context is unbounded.
 | ||
|  |     Passthru,
 | ||
|  |     /// Only show a certain number of lines before and after each match.
 | ||
|  |     Limited(ContextModeLimited),
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl Default for ContextMode {
 | ||
|  |     fn default() -> ContextMode {
 | ||
|  |         ContextMode::Limited(ContextModeLimited::default())
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl ContextMode {
 | ||
|  |     /// Set the "before" context.
 | ||
|  |     ///
 | ||
|  |     /// If this was set to "passthru" context, then it is overridden in favor
 | ||
|  |     /// of limited context with the given value for "before" and `0` for
 | ||
|  |     /// "after."
 | ||
|  |     pub(crate) fn set_before(&mut self, lines: usize) {
 | ||
|  |         match *self {
 | ||
|  |             ContextMode::Passthru => {
 | ||
|  |                 *self = ContextMode::Limited(ContextModeLimited {
 | ||
|  |                     before: Some(lines),
 | ||
|  |                     after: None,
 | ||
|  |                     both: None,
 | ||
|  |                 })
 | ||
|  |             }
 | ||
|  |             ContextMode::Limited(ContextModeLimited {
 | ||
|  |                 ref mut before,
 | ||
|  |                 ..
 | ||
|  |             }) => *before = Some(lines),
 | ||
|  |         }
 | ||
|  |     }
 | ||
|  | 
 | ||
|  |     /// Set the "after" context.
 | ||
|  |     ///
 | ||
|  |     /// If this was set to "passthru" context, then it is overridden in favor
 | ||
|  |     /// of limited context with the given value for "after" and `0` for
 | ||
|  |     /// "before."
 | ||
|  |     pub(crate) fn set_after(&mut self, lines: usize) {
 | ||
|  |         match *self {
 | ||
|  |             ContextMode::Passthru => {
 | ||
|  |                 *self = ContextMode::Limited(ContextModeLimited {
 | ||
|  |                     before: None,
 | ||
|  |                     after: Some(lines),
 | ||
|  |                     both: None,
 | ||
|  |                 })
 | ||
|  |             }
 | ||
|  |             ContextMode::Limited(ContextModeLimited {
 | ||
|  |                 ref mut after, ..
 | ||
|  |             }) => *after = Some(lines),
 | ||
|  |         }
 | ||
|  |     }
 | ||
|  | 
 | ||
|  |     /// Set the "both" context.
 | ||
|  |     ///
 | ||
|  |     /// If this was set to "passthru" context, then it is overridden in favor
 | ||
|  |     /// of limited context with the given value for "both" and `None` for
 | ||
|  |     /// "before" and "after".
 | ||
|  |     pub(crate) fn set_both(&mut self, lines: usize) {
 | ||
|  |         match *self {
 | ||
|  |             ContextMode::Passthru => {
 | ||
|  |                 *self = ContextMode::Limited(ContextModeLimited {
 | ||
|  |                     before: None,
 | ||
|  |                     after: None,
 | ||
|  |                     both: Some(lines),
 | ||
|  |                 })
 | ||
|  |             }
 | ||
|  |             ContextMode::Limited(ContextModeLimited {
 | ||
|  |                 ref mut both, ..
 | ||
|  |             }) => *both = Some(lines),
 | ||
|  |         }
 | ||
|  |     }
 | ||
|  | 
 | ||
|  |     /// A convenience function for use in tests that returns the limited
 | ||
|  |     /// context. If this mode isn't limited, then it panics.
 | ||
|  |     #[cfg(test)]
 | ||
|  |     pub(crate) fn get_limited(&self) -> (usize, usize) {
 | ||
|  |         match *self {
 | ||
|  |             ContextMode::Passthru => unreachable!("context mode is passthru"),
 | ||
|  |             ContextMode::Limited(ref limited) => limited.get(),
 | ||
|  |         }
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// A context mode for a finite number of lines.
 | ||
|  | ///
 | ||
|  | /// Namely, this indicates that a specific number of lines (possibly zero)
 | ||
|  | /// should be shown before and/or after each matching line.
 | ||
|  | ///
 | ||
|  | /// Note that there is a subtle difference between `Some(0)` and `None`. In the
 | ||
|  | /// former case, it happens when `0` is given explicitly, where as `None` is
 | ||
|  | /// the default value and occurs when no value is specified.
 | ||
|  | ///
 | ||
|  | /// `both` is only set by the -C/--context flag. The reason why we don't just
 | ||
|  | /// set before = after = --context is because the before and after context
 | ||
|  | /// settings always take precedent over the -C/--context setting, regardless of
 | ||
|  | /// order. Thus, we need to keep track of them separately.
 | ||
|  | #[derive(Debug, Default, Eq, PartialEq)]
 | ||
|  | pub(crate) struct ContextModeLimited {
 | ||
|  |     before: Option<usize>,
 | ||
|  |     after: Option<usize>,
 | ||
|  |     both: Option<usize>,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl ContextModeLimited {
 | ||
|  |     /// Returns the specific number of contextual lines that should be shown
 | ||
|  |     /// around each match. This takes proper precedent into account, i.e.,
 | ||
|  |     /// that `before` and `after` both partially override `both` in all cases.
 | ||
|  |     ///
 | ||
|  |     /// By default, this returns `(0, 0)`.
 | ||
|  |     pub(crate) fn get(&self) -> (usize, usize) {
 | ||
|  |         let (mut before, mut after) =
 | ||
|  |             self.both.map(|lines| (lines, lines)).unwrap_or((0, 0));
 | ||
|  |         // --before and --after always override --context, regardless
 | ||
|  |         // of where they appear relative to each other.
 | ||
|  |         if let Some(lines) = self.before {
 | ||
|  |             before = lines;
 | ||
|  |         }
 | ||
|  |         if let Some(lines) = self.after {
 | ||
|  |             after = lines;
 | ||
|  |         }
 | ||
|  |         (before, after)
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// Represents the separator to use between non-contiguous sections of
 | ||
|  | /// contextual lines.
 | ||
|  | ///
 | ||
|  | /// The default is `--`.
 | ||
|  | #[derive(Clone, Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) struct ContextSeparator(Option<BString>);
 | ||
|  | 
 | ||
|  | impl Default for ContextSeparator {
 | ||
|  |     fn default() -> ContextSeparator {
 | ||
|  |         ContextSeparator(Some(BString::from("--")))
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl ContextSeparator {
 | ||
|  |     /// Create a new context separator from the user provided argument. This
 | ||
|  |     /// handles unescaping.
 | ||
|  |     pub(crate) fn new(os: &OsStr) -> anyhow::Result<ContextSeparator> {
 | ||
|  |         let Some(string) = os.to_str() else {
 | ||
|  |             anyhow::bail!(
 | ||
|  |                 "separator must be valid UTF-8 (use escape sequences \
 | ||
|  |                  to provide a separator that is not valid UTF-8)"
 | ||
|  |             )
 | ||
|  |         };
 | ||
|  |         Ok(ContextSeparator(Some(Vec::unescape_bytes(string).into())))
 | ||
|  |     }
 | ||
|  | 
 | ||
|  |     /// Creates a new separator that intructs the printer to disable contextual
 | ||
|  |     /// separators entirely.
 | ||
|  |     pub(crate) fn disabled() -> ContextSeparator {
 | ||
|  |         ContextSeparator(None)
 | ||
|  |     }
 | ||
|  | 
 | ||
|  |     /// Return the raw bytes of this separator.
 | ||
|  |     ///
 | ||
|  |     /// If context separators were disabled, then this returns `None`.
 | ||
|  |     ///
 | ||
|  |     /// Note that this may return a `Some` variant with zero bytes.
 | ||
|  |     pub(crate) fn into_bytes(self) -> Option<Vec<u8>> {
 | ||
|  |         self.0.map(|sep| sep.into())
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// The encoding mode the searcher will use.
 | ||
|  | ///
 | ||
|  | /// The default is `Auto`.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum EncodingMode {
 | ||
|  |     /// Use only BOM sniffing to auto-detect an encoding.
 | ||
|  |     Auto,
 | ||
|  |     /// Use an explicit encoding forcefully, but let BOM sniffing override it.
 | ||
|  |     Some(grep::searcher::Encoding),
 | ||
|  |     /// Use no explicit encoding and disable all BOM sniffing. This will
 | ||
|  |     /// always result in searching the raw bytes, regardless of their
 | ||
|  |     /// true encoding.
 | ||
|  |     Disabled,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl Default for EncodingMode {
 | ||
|  |     fn default() -> EncodingMode {
 | ||
|  |         EncodingMode::Auto
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// The regex engine to use.
 | ||
|  | ///
 | ||
|  | /// The default is `Default`.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum EngineChoice {
 | ||
|  |     /// Uses the default regex engine: Rust's `regex` crate.
 | ||
|  |     ///
 | ||
|  |     /// (Well, technically it uses `regex-automata`, but `regex-automata` is
 | ||
|  |     /// the implementation of the `regex` crate.)
 | ||
|  |     Default,
 | ||
|  |     /// Dynamically select the right engine to use.
 | ||
|  |     ///
 | ||
|  |     /// This works by trying to use the default engine, and if the pattern does
 | ||
|  |     /// not compile, it switches over to the PCRE2 engine if it's available.
 | ||
|  |     Auto,
 | ||
|  |     /// Uses the PCRE2 regex engine if it's available.
 | ||
|  |     PCRE2,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl Default for EngineChoice {
 | ||
|  |     fn default() -> EngineChoice {
 | ||
|  |         EngineChoice::Default
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// The field context separator to use to between metadata for each contextual
 | ||
|  | /// line.
 | ||
|  | ///
 | ||
|  | /// The default is `-`.
 | ||
|  | #[derive(Clone, Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) struct FieldContextSeparator(BString);
 | ||
|  | 
 | ||
|  | impl Default for FieldContextSeparator {
 | ||
|  |     fn default() -> FieldContextSeparator {
 | ||
|  |         FieldContextSeparator(BString::from("-"))
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl FieldContextSeparator {
 | ||
|  |     /// Create a new separator from the given argument value provided by the
 | ||
|  |     /// user. Unescaping it automatically handled.
 | ||
|  |     pub(crate) fn new(os: &OsStr) -> anyhow::Result<FieldContextSeparator> {
 | ||
|  |         let Some(string) = os.to_str() else {
 | ||
|  |             anyhow::bail!(
 | ||
|  |                 "separator must be valid UTF-8 (use escape sequences \
 | ||
|  |                  to provide a separator that is not valid UTF-8)"
 | ||
|  |             )
 | ||
|  |         };
 | ||
|  |         Ok(FieldContextSeparator(Vec::unescape_bytes(string).into()))
 | ||
|  |     }
 | ||
|  | 
 | ||
|  |     /// Return the raw bytes of this separator.
 | ||
|  |     ///
 | ||
|  |     /// Note that this may return an empty `Vec`.
 | ||
|  |     pub(crate) fn into_bytes(self) -> Vec<u8> {
 | ||
|  |         self.0.into()
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// The field match separator to use to between metadata for each matching
 | ||
|  | /// line.
 | ||
|  | ///
 | ||
|  | /// The default is `:`.
 | ||
|  | #[derive(Clone, Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) struct FieldMatchSeparator(BString);
 | ||
|  | 
 | ||
|  | impl Default for FieldMatchSeparator {
 | ||
|  |     fn default() -> FieldMatchSeparator {
 | ||
|  |         FieldMatchSeparator(BString::from(":"))
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl FieldMatchSeparator {
 | ||
|  |     /// Create a new separator from the given argument value provided by the
 | ||
|  |     /// user. Unescaping it automatically handled.
 | ||
|  |     pub(crate) fn new(os: &OsStr) -> anyhow::Result<FieldMatchSeparator> {
 | ||
|  |         let Some(string) = os.to_str() else {
 | ||
|  |             anyhow::bail!(
 | ||
|  |                 "separator must be valid UTF-8 (use escape sequences \
 | ||
|  |                  to provide a separator that is not valid UTF-8)"
 | ||
|  |             )
 | ||
|  |         };
 | ||
|  |         Ok(FieldMatchSeparator(Vec::unescape_bytes(string).into()))
 | ||
|  |     }
 | ||
|  | 
 | ||
|  |     /// Return the raw bytes of this separator.
 | ||
|  |     ///
 | ||
|  |     /// Note that this may return an empty `Vec`.
 | ||
|  |     pub(crate) fn into_bytes(self) -> Vec<u8> {
 | ||
|  |         self.0.into()
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// The type of logging to do. `Debug` emits some details while `Trace` emits
 | ||
|  | /// much more.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum LoggingMode {
 | ||
|  |     Debug,
 | ||
|  |     Trace,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// Indicates when to use memory maps.
 | ||
|  | ///
 | ||
|  | /// The default is `Auto`.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum MmapMode {
 | ||
|  |     /// This instructs ripgrep to use heuristics for selecting when to and not
 | ||
|  |     /// to use memory maps for searching.
 | ||
|  |     Auto,
 | ||
|  |     /// This instructs ripgrep to always try memory maps when possible. (Memory
 | ||
|  |     /// maps are not possible to use in all circumstances, for example, for
 | ||
|  |     /// virtual files.)
 | ||
|  |     AlwaysTryMmap,
 | ||
|  |     /// Never use memory maps under any circumstances. This includes even
 | ||
|  |     /// when multi-line search is enabled where ripgrep will read the entire
 | ||
|  |     /// contents of a file on to the heap before searching it.
 | ||
|  |     Never,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl Default for MmapMode {
 | ||
|  |     fn default() -> MmapMode {
 | ||
|  |         MmapMode::Auto
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// Represents a source of patterns that ripgrep should search for.
 | ||
|  | ///
 | ||
|  | /// The reason to unify these is so that we can retain the order of `-f/--flag`
 | ||
|  | /// and `-e/--regexp` flags relative to one another.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum PatternSource {
 | ||
|  |     /// Comes from the `-e/--regexp` flag.
 | ||
|  |     Regexp(String),
 | ||
|  |     /// Comes from the `-f/--file` flag.
 | ||
|  |     File(PathBuf),
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// The sort criteria, if present.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) struct SortMode {
 | ||
|  |     /// Whether to reverse the sort criteria (i.e., descending order).
 | ||
|  |     pub(crate) reverse: bool,
 | ||
|  |     /// The actual sorting criteria.
 | ||
|  |     pub(crate) kind: SortModeKind,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// The criteria to use for sorting.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum SortModeKind {
 | ||
|  |     /// Sort by path.
 | ||
|  |     Path,
 | ||
|  |     /// Sort by last modified time.
 | ||
|  |     LastModified,
 | ||
|  |     /// Sort by last accessed time.
 | ||
|  |     LastAccessed,
 | ||
|  |     /// Sort by creation time.
 | ||
|  |     Created,
 | ||
|  | }
 | ||
|  | 
 | ||
|  | impl SortMode {
 | ||
|  |     /// Checks whether the selected sort mode is supported. If it isn't, an
 | ||
|  |     /// error (hopefully explaining why) is returned.
 | ||
|  |     pub(crate) fn supported(&self) -> anyhow::Result<()> {
 | ||
|  |         match self.kind {
 | ||
|  |             SortModeKind::Path => Ok(()),
 | ||
|  |             SortModeKind::LastModified => {
 | ||
|  |                 let md = std::env::current_exe()
 | ||
|  |                     .and_then(|p| p.metadata())
 | ||
|  |                     .and_then(|md| md.modified());
 | ||
|  |                 let Err(err) = md else { return Ok(()) };
 | ||
|  |                 anyhow::bail!(
 | ||
|  |                     "sorting by last modified isn't supported: {err}"
 | ||
|  |                 );
 | ||
|  |             }
 | ||
|  |             SortModeKind::LastAccessed => {
 | ||
|  |                 let md = std::env::current_exe()
 | ||
|  |                     .and_then(|p| p.metadata())
 | ||
|  |                     .and_then(|md| md.accessed());
 | ||
|  |                 let Err(err) = md else { return Ok(()) };
 | ||
|  |                 anyhow::bail!(
 | ||
|  |                     "sorting by last accessed isn't supported: {err}"
 | ||
|  |                 );
 | ||
|  |             }
 | ||
|  |             SortModeKind::Created => {
 | ||
|  |                 let md = std::env::current_exe()
 | ||
|  |                     .and_then(|p| p.metadata())
 | ||
|  |                     .and_then(|md| md.created());
 | ||
|  |                 let Err(err) = md else { return Ok(()) };
 | ||
|  |                 anyhow::bail!(
 | ||
|  |                     "sorting by creation time isn't supported: {err}"
 | ||
|  |                 );
 | ||
|  |             }
 | ||
|  |         }
 | ||
|  |     }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | /// A single instance of either a change or a selection of one ripgrep's
 | ||
|  | /// file types.
 | ||
|  | #[derive(Debug, Eq, PartialEq)]
 | ||
|  | pub(crate) enum TypeChange {
 | ||
|  |     /// Clear the given type from ripgrep.
 | ||
|  |     Clear { name: String },
 | ||
|  |     /// Add the given type definition (name and glob) to ripgrep.
 | ||
|  |     Add { def: String },
 | ||
|  |     /// Select the given type for filtering.
 | ||
|  |     Select { name: String },
 | ||
|  |     /// Select the given type for filtering but negate it.
 | ||
|  |     Negate { name: String },
 | ||
|  | }
 |