1use crate::api::{metadata, ApiManager};
2use crate::arg::CliInput;
3use clap::{command, Arg, Command};
4
5pub fn cmd() -> Command {
6 cmd_base().subcommand(cmd_api_stub())
7}
8
9fn cmd_base() -> Command {
10 command!()
11 .subcommand_required(true)
12 .arg_required_else_help(true)
13 .disable_help_subcommand(true)
14}
15
16fn cmd_api_stub() -> Command {
17 cmd_api_base().disable_help_flag(true).arg(
18 Arg::new("args")
19 .num_args(0..)
20 .trailing_var_arg(true)
21 .allow_hyphen_values(true)
22 .hide(true),
23 )
24}
25
26fn cmd_api_base_real() -> Command {
27 cmd_api_base()
28 .subcommand_required(true)
29 .arg_required_else_help(true)
30}
31
32pub fn cmd_api_base() -> Command {
33 Command::new("api").about("Directly invoke the Azure API primitives.")
34}
35
36pub fn cmd_api(api_manager: &ApiManager, input: &CliInput) -> Command {
37 let pos_args = input.pos_args();
38
39 if pos_args.is_empty() {
41 return cmd_base().subcommand(
42 cmd_api_base_real().subcommands(api_manager.list_rps().iter().map(Command::new)),
43 );
44 }
45 let rp = pos_args.first().unwrap();
46 match api_manager.read_metadata(rp) {
47 Ok(metadata) => {
48 let mut args = pos_args.iter();
49 let mut command_names = vec![];
50
51 let mut cg = metadata::CommandGroup {
53 name: rp.to_string(),
54 command_groups: Some(metadata.command_groups),
55 ..metadata::CommandGroup::default()
56 };
57
58 let mut c: Option<metadata::Command> = None;
59
60 while let Some(arg) = args.next() {
61 command_names.push(arg.to_string());
62
63 if let Some(v) = cg
64 .command_groups
65 .clone()
66 .and_then(|cgs| cgs.iter().find(|cg| cg.name.as_str() == *arg).cloned())
67 {
68 cg = v;
69 } else if let Some(v) = cg
70 .commands
71 .iter()
72 .find(|c| c.name.as_str() == *arg)
73 .cloned()
74 {
75 c = Some(v);
80 break;
81 }
82 }
83
84 let mut command_names_rev = command_names.iter().rev();
85 let mut cmd = Command::new(command_names_rev.next().unwrap());
86 if let Some(c) = c {
87 cmd = cmd.args(build_args(&c.arg_groups));
89 } else {
90 cmd = cmd
93 .subcommands(cg.commands.iter().map(|c| Command::new(c.name.clone())))
94 .subcommand_required(true)
95 .arg_required_else_help(true);
96 if let Some(cgs) = cg.command_groups {
97 cmd = cmd.subcommands(cgs.iter().map(|c| Command::new(c.name.clone())));
98 }
99 }
100 for name in command_names_rev {
101 cmd = Command::new(name.clone())
102 .subcommand(cmd)
103 .subcommand_required(true)
104 .arg_required_else_help(true);
105 }
106 cmd_base().subcommand(cmd_api_base_real().subcommand(cmd))
107 }
108 Err(err) => {
109 dbg!("subcommand construction failed: {}", err);
110 cmd_base().subcommand(cmd_api_base_real().subcommand(Command::new(rp.to_string())))
111 }
112 }
113}
114
115fn build_args(arg_groups: &Vec<metadata::ArgGroup>) -> Vec<Arg> {
116 let mut out = vec![];
117 arg_groups
118 .iter()
119 .for_each(|ag| out.extend(ag.args.iter().map(build_arg)));
120 out
121}
122
123fn build_arg(arg: &metadata::Arg) -> Arg {
124 let mut short: Option<char> = None;
127 let mut short_aliases = vec![];
128 let mut long: Option<String> = None;
129 let mut long_aliases = vec![];
130 arg.options.iter().for_each(|opt| {
131 if opt.len() == 1 {
132 let c = opt.chars().next().unwrap();
133 if short.is_none() {
134 short = Some(c);
135 } else {
136 short_aliases.push(c);
137 }
138 } else {
139 if long.is_none() {
140 long = Some(opt.clone());
141 } else {
142 long_aliases.push(opt.clone());
143 }
144 }
145 });
146 let mut out = Arg::new(arg.var.clone())
147 .value_name("value")
148 .visible_short_aliases(short_aliases)
149 .visible_aliases(long_aliases);
150 if let Some(short) = short {
151 out = out.short(short);
152 }
153 if let Some(long) = long {
154 out = out.long(long);
155 }
156 if let Some(required) = arg.required {
157 out = out.required(required);
158 }
159 if let Some(help) = &arg.help {
160 out = out.help(help.short.clone());
161 }
162 out
163}
164
165#[test]
166fn verify_cmd() {
167 cmd().debug_assert();
168}