use clap::{Args, Parser, Subcommand};
#[derive(Parser, Debug)]
#[command(name = "cargo", bin_name = "cargo", version)]
pub struct Cargo {
#[command(subcommand)]
pub command: CargoCommand,
}
#[derive(Subcommand, Debug)]
pub enum CargoCommand {
Brief(BriefDirect),
}
#[derive(Parser, Debug)]
#[command(
version,
about = "Visibility-aware Rust API extractor for AI agents",
after_help = "Run `cargo brief <subcommand> --help` for subcommand-specific options."
)]
pub struct BriefDirect {
#[arg(short = 'C', long, global = true)]
pub crates: bool,
#[arg(
short = 'F',
long,
value_name = "FEATURES",
global = true,
requires = "crates"
)]
pub features: Option<String>,
#[arg(long, global = true, requires = "crates")]
pub no_default_features: bool,
#[arg(long, global = true, requires = "crates")]
pub no_cache: bool,
#[command(subcommand)]
pub command: BriefCommand,
}
impl BriefDirect {
pub fn remote_opts(&self) -> RemoteOpts {
RemoteOpts {
crates: self.crates,
features: self.features.clone(),
no_default_features: self.no_default_features,
no_cache: self.no_cache,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct RemoteOpts {
pub crates: bool,
pub features: Option<String>,
pub no_default_features: bool,
pub no_cache: bool,
}
#[derive(Subcommand, Debug, Clone)]
pub enum BriefCommand {
#[command(after_help = "\
EXAMPLES:
# Browse the current crate's API (run inside a Cargo project)
cargo brief api
# Browse a specific module in the current crate
cargo brief api self::net::tcp
# Inspect a crates.io dependency (cached after first run)
cargo brief -C api serde@1 --compact
cargo brief -C -F rt,net,io-util api tokio@1
# Browse a specific module of a remote crate
cargo brief -C api tokio@1::net
cargo brief -C -F net api tokio@1 net
# Reduce output verbosity for large crates
cargo brief -C api tokio@1 --compact
cargo brief -C api tokio@1 --doc-lines 1
RESOLUTION RULES:
The <TARGET> argument is resolved as follows:
1. \"self\" → current package (cwd-based detection)
2. \"self::mod\" → current package, specific module
3. \"crate::mod\" → named crate + module in one argument
4. \"src/foo.rs\" → file path auto-converted to module path
5. \"crate_name\" → workspace package (hyphen/underscore normalized)
6. \"unknown_name\" → treated as package name (use \"self::mod\" for modules)
With -C, TARGET is the crate spec (e.g., serde@1, tokio@1.0).
The [MODULE_PATH] argument also accepts file paths (e.g., src/foo.rs).")]
Api(ApiArgs),
#[command(after_help = "\
EXAMPLES:
# Substring search (smart-case: all-lowercase = insensitive)
cargo brief -C search axum@0.8 Router route
cargo brief -C search bevy ShaderRef Material
# OR-match with comma, methods-of
cargo brief search self \"EventReader,EventWriter\"
cargo brief -C search bytes@1 --methods-of Bytes
# Glob, exact match, exclusion
cargo brief search bevy \"Shader*Ref\" # * = 0+ chars, ? = 1 char
cargo brief search bevy \"=Router\" # final :: segment only
cargo brief search bevy -- spawn -test # -- needed for -prefix args
cargo brief search bevy \"*Plugin*,*Resource* -test\"
PATTERN SYNTAX:
Smart-case: all-lowercase → case-insensitive, any uppercase → case-sensitive.
Space = AND, comma = OR. Multiple args are joined with spaces.
Operators (per token):
word substring — path contains \"word\"
w*ld glob — * matches 0+ chars, ? matches 1 char (full-path anchored)
=Name exact — final path segment (after last ::) equals \"Name\"
-term exclude — remove matches (works with substring, glob, or -=exact)
Exclusions are global across all OR groups.")]
Search(SearchArgs),
#[command(after_help = "\
EXAMPLES:
# List example files with their module docs
cargo brief examples self
cargo brief -C examples tokio@1
# Grep for a pattern in example files
cargo brief examples self spawn
cargo brief -C examples hecs spawn_at --context 3
# Multiple patterns are AND-matched (no quotes needed)
cargo brief examples self spawn async
# Include tests and benches directories
cargo brief -C examples serde --tests --benches derive
MATCHING:
Multiple pattern arguments are joined with spaces (AND-matched).
Smart-case: all-lowercase pattern = case-insensitive, any uppercase = case-sensitive.
Without a pattern, lists files with their //! doc comments.")]
Examples(ExamplesArgs),
#[command(after_help = "\
EXAMPLES:
# Summarize the current crate
cargo brief summary self
# Summarize a remote crate
cargo brief -C -F full summary tokio@1
# Summarize a specific module
cargo brief -C summary bevy bevy::ecs")]
Summary(SummaryArgs),
#[command(after_help = "\
EXAMPLES:
# Clear all cached workspaces
cargo brief clean
# Clear caches for a specific crate
cargo brief clean serde")]
Clean(CleanArgs),
}
#[derive(Args, Debug, Clone)]
pub struct TargetArgs {
#[arg(value_name = "TARGET", default_value = "self")]
pub crate_name: String,
pub module_path: Option<String>,
#[arg(long, help_heading = "Local Workspace")]
pub at_package: Option<String>,
#[arg(long, help_heading = "Local Workspace")]
pub at_mod: Option<String>,
#[arg(long, help_heading = "Local Workspace")]
pub manifest_path: Option<String>,
}
#[derive(Args, Debug, Clone)]
pub struct FilterArgs {
#[arg(long, help_heading = "Filtering")]
pub no_structs: bool,
#[arg(long, help_heading = "Filtering")]
pub no_enums: bool,
#[arg(long, help_heading = "Filtering")]
pub no_traits: bool,
#[arg(long, help_heading = "Filtering")]
pub no_functions: bool,
#[arg(long, help_heading = "Filtering")]
pub no_aliases: bool,
#[arg(long, help_heading = "Filtering")]
pub no_constants: bool,
#[arg(long, help_heading = "Filtering")]
pub no_unions: bool,
#[arg(long, help_heading = "Filtering")]
pub no_macros: bool,
#[arg(long, help_heading = "Filtering")]
pub no_docs: bool,
#[arg(long, help_heading = "Filtering")]
pub no_crate_docs: bool,
#[arg(long, value_name = "N", help_heading = "Filtering")]
pub doc_lines: Option<usize>,
#[arg(long, help_heading = "Filtering")]
pub compact: bool,
#[arg(long, help_heading = "Filtering")]
pub verbose_metadata: bool,
#[arg(long)]
pub all: bool,
}
#[derive(Args, Debug, Clone)]
pub struct GlobalArgs {
#[arg(long, default_value = "nightly", help_heading = "Advanced")]
pub toolchain: String,
#[arg(short, long)]
pub verbose: bool,
}
#[derive(Args, Debug, Clone)]
pub struct ApiArgs {
#[command(flatten)]
pub target: TargetArgs,
#[command(flatten)]
pub filter: FilterArgs,
#[command(flatten)]
pub global: GlobalArgs,
#[arg(long, default_value = "1")]
pub depth: u32,
#[arg(long)]
pub recursive: bool,
#[arg(long)]
pub no_expand_glob: bool,
}
#[derive(Args, Debug, Clone)]
pub struct SearchArgs {
#[arg(value_name = "TARGET", default_value = "self")]
pub crate_name: String,
#[arg(value_name = "PATTERN", num_args = 0..)]
pub patterns: Vec<String>,
#[command(flatten)]
pub filter: FilterArgs,
#[command(flatten)]
pub global: GlobalArgs,
#[arg(long, help_heading = "Local Workspace")]
pub at_package: Option<String>,
#[arg(long, help_heading = "Local Workspace")]
pub at_mod: Option<String>,
#[arg(long, help_heading = "Local Workspace")]
pub manifest_path: Option<String>,
#[arg(long, value_name = "[OFFSET:]N")]
pub limit: Option<String>,
#[arg(long, value_name = "TYPE")]
pub methods_of: Option<String>,
#[arg(long, value_name = "KINDS", help_heading = "Filtering")]
pub search_kind: Option<String>,
#[arg(long)]
pub members: bool,
}
impl SearchArgs {
pub fn pattern(&self) -> String {
self.patterns.join(" ")
}
}
#[derive(Args, Debug, Clone)]
pub struct ExamplesArgs {
#[arg(value_name = "TARGET", default_value = "self")]
pub crate_name: String,
#[arg(value_name = "PATTERN", num_args = 0..)]
pub patterns: Vec<String>,
#[command(flatten)]
pub global: GlobalArgs,
#[arg(long, help_heading = "Local Workspace")]
pub manifest_path: Option<String>,
#[arg(long, default_value = "2")]
pub context: String,
#[arg(long, num_args(0..=1), default_missing_value = "999", value_name = "DEPTH")]
pub tests: Option<u32>,
#[arg(long, num_args(0..=1), default_missing_value = "999", value_name = "DEPTH")]
pub benches: Option<u32>,
}
#[derive(Args, Debug, Clone)]
pub struct SummaryArgs {
#[command(flatten)]
pub target: TargetArgs,
#[command(flatten)]
pub global: GlobalArgs,
}
#[derive(Args, Debug, Clone)]
pub struct CleanArgs {
#[arg(value_name = "SPEC")]
pub spec: Option<String>,
}
impl ExamplesArgs {
pub fn pattern(&self) -> Option<String> {
if self.patterns.is_empty() {
None
} else {
Some(self.patterns.join(" "))
}
}
}