Skip to main content

#
git_derive/
git-derive.rs

    1use std::ffi::OsStr;
    2use std::ffi::OsString;
    3use std::path::PathBuf;
    4
    5use clap::{Args, Parser, Subcommand, ValueEnum};
    6
    7/// A fictional versioning CLI
    8#[derive(Debug, Parser)] // requires `derive` feature
    9#[command(name = "git")]
    10#[command(about = "A fictional versioning CLI", long_about = None)]
    11struct Cli {
    12    #[command(subcommand)]
    13    command: Commands,
    14}
    15
    16#[derive(Debug, Subcommand)]
    17enum Commands {
    18    /// Clones repos
    19    #[command(arg_required_else_help = true)]
    20    Clone {
    21        /// The remote to clone
    22        remote: String,
    23    },
    24    /// Compare two commits
    25    Diff {
    26        #[arg(value_name = "COMMIT")]
    27        base: Option<OsString>,
    28        #[arg(value_name = "COMMIT")]
    29        head: Option<OsString>,
    30        #[arg(last = true)]
    31        path: Option<OsString>,
    32        #[arg(
    33            long,
    34            require_equals = true,
    35            value_name = "WHEN",
    36            num_args = 0..=1,
    37            default_value_t = ColorWhen::Auto,
    38            default_missing_value = "always",
    39            value_enum
    40        )]
    41        color: ColorWhen,
    42    },
    43    /// pushes things
    44    #[command(arg_required_else_help = true)]
    45    Push {
    46        /// The remote to target
    47        remote: String,
    48    },
    49    /// adds things
    50    #[command(arg_required_else_help = true)]
    51    Add {
    52        /// Stuff to add
    53        #[arg(required = true)]
    54        path: Vec<PathBuf>,
    55    },
    56    Stash(StashArgs),
    57    #[command(external_subcommand)]
    58    External(Vec<OsString>),
    59}
    60
    61#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
    62enum ColorWhen {
    63    Always,
    64    Auto,
    65    Never,
    66}
    67
    68impl std::fmt::Display for ColorWhen {
    69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    70        self.to_possible_value()
    71            .expect("no values are skipped")
    72            .get_name()
    73            .fmt(f)
    74    }
    75}
    76
    77#[derive(Debug, Args)]
    78#[command(args_conflicts_with_subcommands = true)]
    79#[command(flatten_help = true)]
    80struct StashArgs {
    81    #[command(subcommand)]
    82    command: Option<StashCommands>,
    83
    84    #[command(flatten)]
    85    push: StashPushArgs,
    86}
    87
    88#[derive(Debug, Subcommand)]
    89enum StashCommands {
    90    Push(StashPushArgs),
    91    Pop { stash: Option<String> },
    92    Apply { stash: Option<String> },
    93}
    94
    95#[derive(Debug, Args)]
    96struct StashPushArgs {
    97    #[arg(short, long)]
    98    message: Option<String>,
    99}
    100
    101fn main() {
    102    let args = Cli::parse();
    103
    104    match args.command {
    105        Commands::Clone { remote } => {
    106            println!("Cloning {remote}");
    107        }
    108        Commands::Diff {
    109            mut base,
    110            mut head,
    111            mut path,
    112            color,
    113        } => {
    114            if path.is_none() {
    115                path = head;
    116                head = None;
    117                if path.is_none() {
    118                    path = base;
    119                    base = None;
    120                }
    121            }
    122            let base = base
    123                .as_deref()
    124                .map(|s| s.to_str().unwrap())
    125                .unwrap_or("stage");
    126            let head = head
    127                .as_deref()
    128                .map(|s| s.to_str().unwrap())
    129                .unwrap_or("worktree");
    130            let path = path.as_deref().unwrap_or_else(|| OsStr::new(""));
    131            println!(
    132                "Diffing {}..{} {} (color={})",
    133                base,
    134                head,
    135                path.to_string_lossy(),
    136                color
    137            );
    138        }
    139        Commands::Push { remote } => {
    140            println!("Pushing to {remote}");
    141        }
    142        Commands::Add { path } => {
    143            println!("Adding {path:?}");
    144        }
    145        Commands::Stash(stash) => {
    146            let stash_cmd = stash.command.unwrap_or(StashCommands::Push(stash.push));
    147            match stash_cmd {
    148                StashCommands::Push(push) => {
    149                    println!("Pushing {push:?}");
    150                }
    151                StashCommands::Pop { stash } => {
    152                    println!("Popping {stash:?}");
    153                }
    154                StashCommands::Apply { stash } => {
    155                    println!("Applying {stash:?}");
    156                }
    157            }
    158        }
    159        Commands::External(args) => {
    160            println!("Calling out to {:?} with {:?}", args[0], &args[1..]);
    161        }
    162    }
    163
    164    // Continued program logic goes here...
    165}