Skip to main content

#
git/
git.rs

    1use std::ffi::OsString;
    2use std::path::PathBuf;
    3
    4use clap::{Command, arg};
    5
    6fn cli() -> Command {
    7    Command::new("git")
    8        .about("A fictional versioning CLI")
    9        .subcommand_required(true)
    10        .arg_required_else_help(true)
    11        .allow_external_subcommands(true)
    12        .subcommand(
    13            Command::new("clone")
    14                .about("Clones repos")
    15                .arg(arg!(<REMOTE> "The remote to clone"))
    16                .arg_required_else_help(true),
    17        )
    18        .subcommand(
    19            Command::new("diff")
    20                .about("Compare two commits")
    21                .arg(arg!(base: [COMMIT]))
    22                .arg(arg!(head: [COMMIT]))
    23                .arg(arg!(path: [PATH]).last(true))
    24                .arg(
    25                    arg!(--color <WHEN>)
    26                        .value_parser(["always", "auto", "never"])
    27                        .num_args(0..=1)
    28                        .require_equals(true)
    29                        .default_value("auto")
    30                        .default_missing_value("always"),
    31                ),
    32        )
    33        .subcommand(
    34            Command::new("push")
    35                .about("pushes things")
    36                .arg(arg!(<REMOTE> "The remote to target"))
    37                .arg_required_else_help(true),
    38        )
    39        .subcommand(
    40            Command::new("add")
    41                .about("adds things")
    42                .arg_required_else_help(true)
    43                .arg(arg!(<PATH> ... "Stuff to add").value_parser(clap::value_parser!(PathBuf))),
    44        )
    45        .subcommand(
    46            Command::new("stash")
    47                .args_conflicts_with_subcommands(true)
    48                .flatten_help(true)
    49                .args(push_args())
    50                .subcommand(Command::new("push").args(push_args()))
    51                .subcommand(Command::new("pop").arg(arg!([STASH])))
    52                .subcommand(Command::new("apply").arg(arg!([STASH]))),
    53        )
    54}
    55
    56fn push_args() -> Vec<clap::Arg> {
    57    vec![arg!(-m --message <MESSAGE>)]
    58}
    59
    60fn main() {
    61    let matches = cli().get_matches();
    62
    63    match matches.subcommand() {
    64        Some(("clone", sub_matches)) => {
    65            println!(
    66                "Cloning {}",
    67                sub_matches.get_one::<String>("REMOTE").expect("required")
    68            );
    69        }
    70        Some(("diff", sub_matches)) => {
    71            let color = sub_matches
    72                .get_one::<String>("color")
    73                .map(|s| s.as_str())
    74                .expect("defaulted in clap");
    75
    76            let mut base = sub_matches.get_one::<String>("base").map(|s| s.as_str());
    77            let mut head = sub_matches.get_one::<String>("head").map(|s| s.as_str());
    78            let mut path = sub_matches.get_one::<String>("path").map(|s| s.as_str());
    79            if path.is_none() {
    80                path = head;
    81                head = None;
    82                if path.is_none() {
    83                    path = base;
    84                    base = None;
    85                }
    86            }
    87            let base = base.unwrap_or("stage");
    88            let head = head.unwrap_or("worktree");
    89            let path = path.unwrap_or("");
    90            println!("Diffing {base}..{head} {path} (color={color})");
    91        }
    92        Some(("push", sub_matches)) => {
    93            println!(
    94                "Pushing to {}",
    95                sub_matches.get_one::<String>("REMOTE").expect("required")
    96            );
    97        }
    98        Some(("add", sub_matches)) => {
    99            let paths = sub_matches
    100                .get_many::<PathBuf>("PATH")
    101                .into_iter()
    102                .flatten()
    103                .collect::<Vec<_>>();
    104            println!("Adding {paths:?}");
    105        }
    106        Some(("stash", sub_matches)) => {
    107            let stash_command = sub_matches.subcommand().unwrap_or(("push", sub_matches));
    108            match stash_command {
    109                ("apply", sub_matches) => {
    110                    let stash = sub_matches.get_one::<String>("STASH");
    111                    println!("Applying {stash:?}");
    112                }
    113                ("pop", sub_matches) => {
    114                    let stash = sub_matches.get_one::<String>("STASH");
    115                    println!("Popping {stash:?}");
    116                }
    117                ("push", sub_matches) => {
    118                    let message = sub_matches.get_one::<String>("message");
    119                    println!("Pushing {message:?}");
    120                }
    121                (name, _) => {
    122                    unreachable!("Unsupported subcommand `{name}`")
    123                }
    124            }
    125        }
    126        Some((ext, sub_matches)) => {
    127            let args = sub_matches
    128                .get_many::<OsString>("")
    129                .into_iter()
    130                .flatten()
    131                .collect::<Vec<_>>();
    132            println!("Calling out to {ext:?} with {args:?}");
    133        }
    134        _ => unreachable!(), // If all subcommands are defined above, anything else is unreachable!()
    135    }
    136
    137    // Continued program logic goes here...
    138}