grit_lib/parse_options_test_tool/
flags_cmd.rs1use super::parse_options_cmd::ParseOptionsToolError;
4
5const KEEP_DASHDASH: u32 = 1 << 0;
6const STOP_AT_NON_OPTION: u32 = 1 << 1;
7const KEEP_ARGV0: u32 = 1 << 2;
8const KEEP_UNKNOWN_OPT: u32 = 1 << 3;
9const NO_INTERNAL_HELP: u32 = 1 << 4;
10
11pub fn run_parse_options_flags(args: &[String]) -> Result<i32, ParseOptionsToolError> {
13 if args.len() < 2 {
14 return Err(ParseOptionsToolError::Fatal(
15 "error: 'cmd' is mandatory\nusage: test-tool parse-options-flags [flag-options] cmd [options]\n"
16 .to_string(),
17 ));
18 }
19 let mut i = 1usize;
20 let mut test_flags: u32 = 0;
21 while i < args.len() {
22 let a = &args[i];
23 if !a.starts_with("--") {
24 break;
25 }
26 match a.as_str() {
27 "--keep-dashdash" => test_flags |= KEEP_DASHDASH,
28 "--stop-at-non-option" => test_flags |= STOP_AT_NON_OPTION,
29 "--keep-argv0" => test_flags |= KEEP_ARGV0,
30 "--keep-unknown-opt" => test_flags |= KEEP_UNKNOWN_OPT,
31 "--no-internal-help" => test_flags |= NO_INTERNAL_HELP,
32 "--subcommand-optional" => test_flags |= 1 << 7,
33 _ => {
34 return Err(ParseOptionsToolError::Fatal(format!(
35 "error: unknown option `{a}'\n"
36 )));
37 }
38 }
39 i += 1;
40 }
41 if args.get(i).map(|s| s.as_str()) != Some("cmd") {
42 return Err(ParseOptionsToolError::Fatal(
43 "error: 'cmd' is mandatory\nusage: test-tool parse-options-flags [flag-options] cmd [options]\n"
44 .to_string(),
45 ));
46 }
47 parse_flags_cmd_inner(&args[i..], test_flags)
48}
49
50fn parse_int_opt(s: &str) -> Result<i32, ParseOptionsToolError> {
51 s.parse().map_err(|_| {
52 ParseOptionsToolError::Fatal("error: option `opt' expects a numerical value\n".to_string())
53 })
54}
55
56fn parse_flags_cmd_inner(argv: &[String], flags: u32) -> Result<i32, ParseOptionsToolError> {
57 if argv.is_empty() || argv[0] != "cmd" {
58 return Err(ParseOptionsToolError::Fatal(
59 "error: 'cmd' is mandatory\nusage: test-tool parse-options-flags [flag-options] cmd [options]\n"
60 .to_string(),
61 ));
62 }
63
64 let keep_dashdash = flags & KEEP_DASHDASH != 0;
65 let stop_at_non = flags & STOP_AT_NON_OPTION != 0;
66 let keep_argv0 = flags & KEEP_ARGV0 != 0;
67 let keep_unknown = flags & KEEP_UNKNOWN_OPT != 0;
68 let no_internal_help = flags & NO_INTERNAL_HELP != 0;
69 let internal_help = !no_internal_help;
70
71 let total_after_cmd = argv.len().saturating_sub(1);
72 let mut opt: i32 = 0;
73 let mut i = 1usize;
74 let mut out: Vec<String> = Vec::new();
75 if keep_argv0 {
76 out.push("cmd".to_string());
77 }
78
79 while i < argv.len() {
80 let arg = &argv[i];
81
82 if internal_help && total_after_cmd == 1 && arg == "-h" {
83 return Err(ParseOptionsToolError::Help);
84 }
85
86 if arg == "-" || !arg.starts_with('-') {
87 if stop_at_non {
88 out.extend(argv[i..].iter().cloned());
89 break;
90 }
91 out.push(arg.clone());
92 i += 1;
93 continue;
94 }
95
96 if arg == "--" {
97 if keep_dashdash {
98 out.push("--".to_string());
99 i += 1;
100 out.extend(argv[i..].iter().cloned());
101 } else {
102 i += 1;
103 out.extend(argv[i..].iter().cloned());
104 }
105 break;
106 }
107
108 if arg == "--end-of-options" {
109 if !keep_unknown {
110 i += 1;
111 out.extend(argv[i..].iter().cloned());
112 } else {
113 out.push(arg.clone());
114 i += 1;
115 out.extend(argv[i..].iter().cloned());
116 }
117 break;
118 }
119
120 if arg.starts_with("--") {
121 let name = arg.strip_prefix("--").unwrap_or(arg.as_str());
122 if internal_help && (name == "help" || name == "help-all") {
123 return Err(ParseOptionsToolError::Help);
124 }
125 if let Some(rest) = name.strip_prefix("opt=") {
126 opt = parse_int_opt(rest)?;
127 i += 1;
128 continue;
129 }
130 if name == "opt" {
131 i += 1;
132 let v = argv.get(i).ok_or_else(|| {
133 ParseOptionsToolError::Fatal("error: option `opt' requires a value\n".to_string())
134 })?;
135 opt = parse_int_opt(v)?;
136 i += 1;
137 continue;
138 }
139 if keep_unknown {
140 out.push(arg.clone());
141 i += 1;
142 continue;
143 }
144 let key = name.split('=').next().unwrap_or(name);
145 return Err(ParseOptionsToolError::Fatal(format!(
146 "error: unknown option `{key}'\nusage: <...> cmd [options]\n"
147 )));
148 }
149
150 let body = &arg[1..];
152 if let Some(rest) = body.strip_prefix('o') {
153 if rest.is_empty() {
154 i += 1;
155 let v = argv.get(i).ok_or_else(|| {
156 ParseOptionsToolError::Fatal("error: switch `o' requires a value\n".to_string())
157 })?;
158 opt = parse_int_opt(v)?;
159 i += 1;
160 } else {
161 opt = parse_int_opt(rest)?;
162 i += 1;
163 }
164 continue;
165 }
166 if (body == "h" || (internal_help && body.starts_with('h'))) && internal_help {
167 return Err(ParseOptionsToolError::Help);
168 }
169 if no_internal_help && (body == "h" || body.starts_with('h')) {
170 if keep_unknown {
171 out.push(arg.clone());
172 i += 1;
173 continue;
174 }
175 return Err(ParseOptionsToolError::Fatal("error: unknown switch `h'\nusage: <...> cmd [options]\n".to_string()));
176 }
177 if keep_unknown {
178 out.push(arg.clone());
179 i += 1;
180 continue;
181 }
182 let c = body.chars().next().unwrap_or('?');
183 return Err(ParseOptionsToolError::Fatal(format!(
184 "error: unknown switch `{c}'\nusage: <...> cmd [options]\n"
185 )));
186 }
187
188 println!("opt: {opt}");
189 for (idx, a) in out.iter().enumerate() {
190 println!("arg {:02}: {a}", idx);
191 }
192 Ok(0)
193}