| use std::ffi::OsStr; |
| use std::ffi::OsString; |
| use std::path::PathBuf; |
| |
| use clap::{Args, Parser, Subcommand, ValueEnum}; |
| |
| /// A fictional versioning CLI |
| #[derive(Debug, Parser)] // requires `derive` feature |
| #[command(name = "git")] |
| #[command(about = "A fictional versioning CLI", long_about = None)] |
| struct Cli { |
| #[command(subcommand)] |
| command: Commands, |
| } |
| |
| #[derive(Debug, Subcommand)] |
| enum Commands { |
| /// Clones repos |
| #[command(arg_required_else_help = true)] |
| Clone { |
| /// The remote to clone |
| remote: String, |
| }, |
| /// Compare two commits |
| Diff { |
| #[arg(value_name = "COMMIT")] |
| base: Option<OsString>, |
| #[arg(value_name = "COMMIT")] |
| head: Option<OsString>, |
| #[arg(last = true)] |
| path: Option<OsString>, |
| #[arg( |
| long, |
| require_equals = true, |
| value_name = "WHEN", |
| num_args = 0..=1, |
| default_value_t = ColorWhen::Auto, |
| default_missing_value = "always", |
| value_enum |
| )] |
| color: ColorWhen, |
| }, |
| /// pushes things |
| #[command(arg_required_else_help = true)] |
| Push { |
| /// The remote to target |
| remote: String, |
| }, |
| /// adds things |
| #[command(arg_required_else_help = true)] |
| Add { |
| /// Stuff to add |
| #[arg(required = true)] |
| path: Vec<PathBuf>, |
| }, |
| Stash(StashArgs), |
| #[command(external_subcommand)] |
| External(Vec<OsString>), |
| } |
| |
| #[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)] |
| enum ColorWhen { |
| Always, |
| Auto, |
| Never, |
| } |
| |
| impl std::fmt::Display for ColorWhen { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| self.to_possible_value() |
| .expect("no values are skipped") |
| .get_name() |
| .fmt(f) |
| } |
| } |
| |
| #[derive(Debug, Args)] |
| #[command(args_conflicts_with_subcommands = true)] |
| #[command(flatten_help = true)] |
| struct StashArgs { |
| #[command(subcommand)] |
| command: Option<StashCommands>, |
| |
| #[command(flatten)] |
| push: StashPushArgs, |
| } |
| |
| #[derive(Debug, Subcommand)] |
| enum StashCommands { |
| Push(StashPushArgs), |
| Pop { stash: Option<String> }, |
| Apply { stash: Option<String> }, |
| } |
| |
| #[derive(Debug, Args)] |
| struct StashPushArgs { |
| #[arg(short, long)] |
| message: Option<String>, |
| } |
| |
| fn main() { |
| let args = Cli::parse(); |
| |
| match args.command { |
| Commands::Clone { remote } => { |
| println!("Cloning {remote}"); |
| } |
| Commands::Diff { |
| mut base, |
| mut head, |
| mut path, |
| color, |
| } => { |
| if path.is_none() { |
| path = head; |
| head = None; |
| if path.is_none() { |
| path = base; |
| base = None; |
| } |
| } |
| let base = base |
| .as_deref() |
| .map(|s| s.to_str().unwrap()) |
| .unwrap_or("stage"); |
| let head = head |
| .as_deref() |
| .map(|s| s.to_str().unwrap()) |
| .unwrap_or("worktree"); |
| let path = path.as_deref().unwrap_or_else(|| OsStr::new("")); |
| println!( |
| "Diffing {}..{} {} (color={})", |
| base, |
| head, |
| path.to_string_lossy(), |
| color |
| ); |
| } |
| Commands::Push { remote } => { |
| println!("Pushing to {remote}"); |
| } |
| Commands::Add { path } => { |
| println!("Adding {path:?}"); |
| } |
| Commands::Stash(stash) => { |
| let stash_cmd = stash.command.unwrap_or(StashCommands::Push(stash.push)); |
| match stash_cmd { |
| StashCommands::Push(push) => { |
| println!("Pushing {push:?}"); |
| } |
| StashCommands::Pop { stash } => { |
| println!("Popping {stash:?}"); |
| } |
| StashCommands::Apply { stash } => { |
| println!("Applying {stash:?}"); |
| } |
| } |
| } |
| Commands::External(args) => { |
| println!("Calling out to {:?} with {:?}", &args[0], &args[1..]); |
| } |
| } |
| |
| // Continued program logic goes here... |
| } |