1 use std::ffi::OsString;
2 use std::path::PathBuf;
3 
4 use clap::{arg, Command};
5 
cli() -> Command<'static>6 fn cli() -> Command<'static> {
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         .allow_invalid_utf8_for_external_subcommands(true)
13         .subcommand(
14             Command::new("clone")
15                 .about("Clones repos")
16                 .arg(arg!(<REMOTE> "The remote to clone"))
17                 .arg_required_else_help(true),
18         )
19         .subcommand(
20             Command::new("push")
21                 .about("pushes things")
22                 .arg(arg!(<REMOTE> "The remote to target"))
23                 .arg_required_else_help(true),
24         )
25         .subcommand(
26             Command::new("add")
27                 .about("adds things")
28                 .arg_required_else_help(true)
29                 .arg(arg!(<PATH> ... "Stuff to add").value_parser(clap::value_parser!(PathBuf))),
30         )
31         .subcommand(
32             Command::new("stash")
33                 .args_conflicts_with_subcommands(true)
34                 .args(push_args())
35                 .subcommand(Command::new("push").args(push_args()))
36                 .subcommand(Command::new("pop").arg(arg!([STASH])))
37                 .subcommand(Command::new("apply").arg(arg!([STASH]))),
38         )
39 }
40 
push_args() -> Vec<clap::Arg<'static>>41 fn push_args() -> Vec<clap::Arg<'static>> {
42     vec![arg!(-m --message <MESSAGE>).required(false)]
43 }
44 
main()45 fn main() {
46     let matches = cli().get_matches();
47 
48     match matches.subcommand() {
49         Some(("clone", sub_matches)) => {
50             println!(
51                 "Cloning {}",
52                 sub_matches.get_one::<String>("REMOTE").expect("required")
53             );
54         }
55         Some(("push", sub_matches)) => {
56             println!(
57                 "Pushing to {}",
58                 sub_matches.get_one::<String>("REMOTE").expect("required")
59             );
60         }
61         Some(("add", sub_matches)) => {
62             let paths = sub_matches
63                 .get_many::<PathBuf>("PATH")
64                 .into_iter()
65                 .flatten()
66                 .collect::<Vec<_>>();
67             println!("Adding {:?}", paths);
68         }
69         Some(("stash", sub_matches)) => {
70             let stash_command = sub_matches.subcommand().unwrap_or(("push", sub_matches));
71             match stash_command {
72                 ("apply", sub_matches) => {
73                     let stash = sub_matches.get_one::<String>("STASH");
74                     println!("Applying {:?}", stash);
75                 }
76                 ("pop", sub_matches) => {
77                     let stash = sub_matches.get_one::<String>("STASH");
78                     println!("Popping {:?}", stash);
79                 }
80                 ("push", sub_matches) => {
81                     let message = sub_matches.get_one::<String>("message");
82                     println!("Pushing {:?}", message);
83                 }
84                 (name, _) => {
85                     unreachable!("Unsupported subcommand `{}`", name)
86                 }
87             }
88         }
89         Some((ext, sub_matches)) => {
90             let args = sub_matches
91                 .get_many::<OsString>("")
92                 .into_iter()
93                 .flatten()
94                 .collect::<Vec<_>>();
95             println!("Calling out to {:?} with {:?}", ext, args);
96         }
97         _ => unreachable!(), // If all subcommands are defined above, anything else is unreachabe!()
98     }
99 
100     // Continued program logic goes here...
101 }
102