Skip to content

ripgrep crates/core/flags/mod.rs: Code Companion

Reference code for the Flag System Architecture lecture. Sections correspond to the lecture document.


Section 1: The Module's Public Contract

/*!
Defines ripgrep's command line interface.

This modules deals with everything involving ripgrep's flags and positional
arguments. This includes generating shell completions, `--help` output and even
ripgrep's man page. It's also responsible for parsing and validating every
flag (including reading ripgrep's config file), and manages the contact points
between these flags and ripgrep's cast of supporting libraries.
*/
use std::{
    ffi::OsString,
    fmt::Debug,
    panic::{RefUnwindSafe, UnwindSafe},
};

// Re-exports organized by purpose: completions, documentation, and parsing
pub(crate) use crate::flags::{
    // Shell completion generators for different shells
    complete::{
        bash::generate as generate_complete_bash,
        fish::generate as generate_complete_fish,
        powershell::generate as generate_complete_powershell,
        zsh::generate as generate_complete_zsh,
    },
    // Documentation generators for help text, man pages, and version info
    doc::{
        help::{
            generate_long as generate_help_long,
            generate_short as generate_help_short,
        },
        man::generate as generate_man_page,
        version::{
            generate_long as generate_version_long,
            generate_pcre2 as generate_version_pcre2,
            generate_short as generate_version_short,
        },
    },
    // High-level parsed arguments - what the rest of ripgrep uses
    hiargs::HiArgs,
    // Low-level argument types for different operational modes
    lowargs::{GenerateMode, Mode, SearchMode, SpecialMode},
    // The parsing infrastructure
    parse::{ParseResult, parse},
};

// Submodule declarations - each handles a single responsibility
mod complete;   // Shell completion generation
mod config;     // Configuration file reading
mod defs;       // Actual flag definitions
mod doc;        // Documentation generation
mod hiargs;     // High-level argument representation
mod lowargs;    // Low-level argument representation
mod parse;      // Command-line parsing

The pub(crate) visibility makes these types available throughout the core crate while hiding them from external consumers. The re-exports with renamed identifiers (e.g., generate as generate_complete_bash) create a clean, descriptive API.


Section 2: The Flag Trait as Architectural Contract

/// A trait that encapsulates the definition of an optional flag for ripgrep.
///
/// This trait is meant to be used via dynamic dispatch. Namely, the `defs`
/// module provides a single global slice of `&dyn Flag` values correspondings
/// to all of the flags in ripgrep.
trait Flag: Debug + Send + Sync + UnwindSafe + RefUnwindSafe + 'static {
    /// Returns true if this flag is a switch. When a flag is a switch, the
    /// CLI parser will not look for a value after the flag is seen.
    fn is_switch(&self) -> bool;

    // ... name methods ...

    /// Returns the category of this flag.
    fn doc_category(&self) -> Category;

    // ... documentation methods ...

    /// Given the parsed value (which might just be a switch), this should
    /// update the state in `args` based on the value given for this flag.
    ///
    /// By convention, implementations should generally not try to "do"
    /// anything other than validate the value given. For example, the
    /// implementation for `--hostname-bin` should not try to resolve the
    /// hostname to use by running the binary provided.
    fn update(
        &self,
        value: FlagValue,
        args: &mut crate::flags::lowargs::LowArgs,
    ) -> anyhow::Result<()>;
}

The trait bounds tell an important story: Debug enables introspection, Send + Sync allows potential parallel processing, UnwindSafe + RefUnwindSafe ensure panic safety, and 'static guarantees flag definitions outlive any references to them.


Section 3: Name Resolution and Aliases

trait Flag: /* bounds */ {
    /// A short single byte name for this flag. This returns `None` by default,
    /// which signifies that the flag has no short name.
    ///
    /// The byte returned must be an ASCII codepoint that is a `.` or is
    /// alpha-numeric.
    fn name_short(&self) -> Option<u8> {
        None  // Default: no short name
    }

    /// Returns the long name of this flag. All flags must have a "long" name.
    ///
    /// The long name must be at least 2 bytes, and all of its bytes must be
    /// ASCII codepoints that are either `-` or alpha-numeric.
    fn name_long(&self) -> &'static str;

    /// Returns a list of aliases for this flag.
    ///
    /// By default, an empty slice is returned.
    fn aliases(&self) -> &'static [&'static str] {
        &[]  // Default: no aliases
    }

    /// Returns a negated name for this flag. The negation of a flag is
    /// intended to have the opposite meaning of a flag or to otherwise turn
    /// something "off" or revert it to its default behavior.
    ///
    /// Negated flags are not listed in their own section in the `-h/--help`
    /// output or man page. Instead, they are automatically mentioned at the
    /// end of the documentation section of the flag they negated.
    fn name_negated(&self) -> Option<&'static str> {
        None  // Default: no negation
    }
}

Note that name_long has no default implementation—it's the only required naming method. The constraint that short names be u8 (not char) enforces ASCII-only short flags, which is a deliberate design choice for portability.


Section 4: Documentation as First-Class Data

trait Flag: /* bounds */ {
    /// Returns the variable name describing the type of value this flag
    /// accepts. This should always be set for non-switch flags and never set
    /// for switch flags.
    ///
    /// For example, the `--max-count` flag has its variable name set to `NUM`.
    ///
    /// The convention is to capitalize variable names.
    fn doc_variable(&self) -> Option<&'static str> {
        None
    }

    /// Returns the category of this flag.
    ///
    /// Every flag must have a single category. Categories are used to organize
    /// flags in the generated documentation.
    fn doc_category(&self) -> Category;

    /// A (very) short documentation string describing what this flag does.
    ///
    /// This may sacrifice "proper English" in order to be as terse as
    /// possible. Generally, we try to ensure that `rg -h` doesn't have any
    /// lines that exceed 79 columns.
    fn doc_short(&self) -> &'static str;

    /// A (possibly very) longer documentation string describing in full
    /// detail what this flag does. This should be in mandoc/mdoc format.
    fn doc_long(&self) -> &'static str;

    /// If this is a non-switch flag that accepts a small set of specific
    /// values, then this should list them.
    fn doc_choices(&self) -> &'static [&'static str] {
        &[]
    }
}

The 'static lifetime on all return types ensures documentation strings are compile-time constants, enabling zero-cost access during help generation. The mandoc format requirement for doc_long enables direct man page generation.


Section 5: The Category System for Organization

/// The category that a flag belongs to.
///
/// Categories are used to organize flags into "logical" groups in the
/// generated documentation.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
enum Category {
    /// Flags related to how ripgrep reads its input.
    Input,
    /// Flags related to the operation of the search itself.
    Search,
    /// Flags related to how ripgrep filters haystacks.
    Filter,
    /// Flags related to how ripgrep shows its search results.
    Output,
    /// Flags related to changing ripgrep's output at a more fundamental level.
    OutputModes,
    /// Flags related to logging behavior.
    Logging,
    /// Other behaviors not related to ripgrep's core functionality.
    OtherBehaviors,
}

impl Category {
    /// Returns a string representation of this category.
    ///
    /// This string is the name of the variable used in various templates for
    /// generated documentation. This name can be used for interpolation.
    fn as_str(&self) -> &'static str {
        match *self {
            Category::Input => "input",
            Category::Search => "search",
            Category::Filter => "filter",
            Category::Output => "output",
            Category::OutputModes => "output-modes",
            Category::Logging => "logging",
            Category::OtherBehaviors => "other-behaviors",
        }
    }
}

The Ord and PartialOrd derives use the enum variant declaration order for comparison, making Input sort before Search, which sorts before Filter, and so on. This determines presentation order in documentation.


Section 6: Shell Completion Support

/// The kind of argument a flag accepts, to be used for shell completions.
#[derive(Clone, Copy, Debug)]
enum CompletionType {
    /// No special category. is_switch() and doc_choices() may apply.
    Other,
    /// A path to a file.
    Filename,
    /// A command in $PATH.
    Executable,
    /// The name of a file type, as used by e.g. --type.
    Filetype,
    /// The name of an encoding_rs encoding, as used by --encoding.
    Encoding,
}

// In the Flag trait:
trait Flag: /* bounds */ {
    fn completion_type(&self) -> CompletionType {
        CompletionType::Other  // Default for most flags
    }
}

Each variant maps to shell-specific completion behavior. For example, Filename triggers file path completion, while Executable searches $PATH. The default Other falls back to doc_choices() if available.


Section 7: The FlagValue Type

/// Represents a value parsed from the command line.
///
/// This doesn't include the corresponding flag, but values come in one of
/// two forms: a switch (on or off) or an arbitrary value.
#[derive(Debug)]
enum FlagValue {
    /// A flag that is either on or off.
    Switch(bool),
    /// A flag that comes with an arbitrary user value.
    Value(OsString),
}

impl FlagValue {
    /// Return the yes or no value of this switch.
    ///
    /// If this flag value is not a switch, then this panics.
    fn unwrap_switch(self) -> bool {
        match self {
            FlagValue::Switch(yes) => yes,
            FlagValue::Value(_) => {
                unreachable!("got flag value but expected switch")
            }
        }
    }

    /// Return the user provided value of this flag.
    ///
    /// If this flag is a switch, then this panics.
    fn unwrap_value(self) -> OsString {
        match self {
            FlagValue::Switch(_) => {
                unreachable!("got switch but expected flag value")
            }
            FlagValue::Value(v) => v,
        }
    }
}

Using OsString instead of String is crucial—it allows flag values to contain arbitrary bytes, which matters on Unix where paths can contain non-UTF-8 sequences. The unreachable! macro in the unwrap methods indicates programmer error rather than user error.


Quick Reference

Flag Trait Method Summary

Method Required Returns Purpose
is_switch Yes bool Determines if flag takes a value
name_short No Option<u8> Single-character alias
name_long Yes &'static str Primary flag name
aliases No &'static [&'static str] Alternative names
name_negated No Option<&'static str> --no-* form
doc_variable No Option<&'static str> Value placeholder (e.g., NUM)
doc_category Yes Category Documentation grouping
doc_short Yes &'static str Brief -h description
doc_long Yes &'static str Full --help/man description
doc_choices No &'static [&'static str] Valid values list
completion_type No CompletionType Shell completion hint
update Yes anyhow::Result<()> Apply flag to args

Trait Bounds on Flag

Flag: Debug + Send + Sync + UnwindSafe + RefUnwindSafe + 'static

Architecture Flow

Command Line Input
    parse::parse()
    LowArgs (raw flag values)
    HiArgs (validated, high-level)
    ripgrep execution