1 | use std::ffi::OsString; |
2 | use std::path::PathBuf; |
3 | |
4 | use clap::{arg, Command}; |
5 | |
6 | fn 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 | |
56 | fn push_args() -> Vec<clap::Arg> { |
57 | vec![arg!(-m --message <MESSAGE>)] |
58 | } |
59 | |
60 | fn 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 | } |
139 | |