1use cmdopts::{parse_opts, CmdOpt, InfoCode, ParseError, ProcessCode};
14
15fn print_usage() {
16 eprintln!(
17 "Usage: {} [OPTION]... [--] FILE1 [FILE2]",
18 std::env::args().next().unwrap()
19 );
20 println!(
21 r"
22Available options:
23-a, --long_a No value option
24-b, --long_b=INT Option with integer value, range [-10..10]
25-c, --long_c=STR Option with string value
26-d Short option with string value
27 --long_e=STR Long option with string value
28-h, --help Print this help
29
30All options are optional. FILE1 is required, FILE2 is not.
31Use -- separator to avoid ambiguity if file name starts with '-'.
32"
33 )
34}
35
36#[derive(Debug)]
37struct Options {
38 a_opt: Option<()>,
39 b_opt: Option<i32>,
40 c_opt: Option<String>,
41 d_opt: Option<String>,
42 e_opt: Option<String>,
43 file1: Option<String>,
44 file2: Option<String>,
45 help: Option<()>,
46}
47
48impl Default for Options {
49 fn default() -> Self {
50 Options {
51 a_opt: None,
52 b_opt: None,
53 c_opt: None,
54 d_opt: None,
55 e_opt: None,
56 file1: None,
57 file2: None,
58 help: None,
59 }
60 }
61}
62
63#[allow(unused_imports)]
64fn process_cmdopts(opts: &mut Options) -> Result<(), ParseError> {
65 use std::cell::Cell;
66 use CmdOpt::*;
67 use InfoCode::*;
68 use ParseError::*;
69 use ProcessCode::*;
70
71 let opt_id = Cell::new('h');
73
74 let mut file_i = 0;
76
77 parse_opts(
78 |opt, constr| match opt {
79 &Short(c) => match c {
80 'a' | 'h' => {
81 opt_id.set(c);
82 NoValueOpt
83 }
84 'b' | 'c' | 'd' => {
85 opt_id.set(c);
86 ValueOpt
87 }
88 '-' => {
89 constr.not_in_group();
90 opt_id.set('-');
91 NoValueOpt
92 }
93 _ => InfoCode::InvalidOpt,
94 },
95 Long(s) => match s.as_str() {
96 "long_a" => {
97 opt_id.set('a');
98 NoValueOpt
99 }
100 "long_b" => {
101 opt_id.set('b');
102 ValueOpt
103 }
104 "long_c" => {
105 opt_id.set('c');
106 ValueOpt
107 }
108 "long_e" => {
109 opt_id.set('e');
110 ValueOpt
111 }
112 "help" => {
113 opt_id.set('h');
114 NoValueOpt
115 }
116 _ => InfoCode::InvalidOpt,
117 },
118 },
119 |opt, val| {
120 let mut switch_mode = false;
122
123 if file_i <= 0 && opt.is_none() {
125 file_i = 1;
126 switch_mode = true;
127 }
128
129 if file_i <= 0 {
130 let mut handled = true;
137
138 match opt_id.get() {
139 'h' => {
141 opts.help = Some(());
142 return Ok(ProcessCode::Break);
143 }
144 '-' => {
145 file_i = 1;
146 return Ok(ProcessCode::ToggleParsingMode);
147 }
148 'a' => {
149 opts.a_opt = Some(());
150 }
151 _ => {
152 handled = false;
153 }
154 }
155 if handled {
156 return Ok(ProcessCode::Continue);
157 }
158
159 let val_str = &val.as_ref().unwrap().val;
162 handled = true;
163
164 match opt_id.get() {
165 'c' => {
166 opts.c_opt = Some(val_str.clone());
167 }
168 'd' => {
169 opts.d_opt = Some(val_str.clone());
170 }
171 'e' => {
172 opts.e_opt = Some(val_str.clone());
173 }
174 _ => {
175 handled = false;
176 }
177 }
178 if handled {
179 return Ok(ProcessCode::Continue);
180 }
181
182 let opt_ref = opt.as_ref().unwrap();
185
186 let val_i: i32 = val_str.parse().map_err(|_| {
187 ParseError::InvalidOpt(opt_ref.clone(), "Integer expected".to_string())
188 })?;
189
190 match opt_id.get() {
191 'b' => {
192 if val_i >= -10 && val_i <= 10 {
193 opts.b_opt = Some(val_i);
194 } else {
195 return Err(ParseError::InvalidOpt(
196 opt_ref.clone(),
197 "Integer in range [-10..10] required".to_string(),
198 ));
199 }
200 }
201 _ => {}
202 }
203
204 Ok(ProcessCode::Continue)
205 } else {
206 let val_str = &val.as_ref().unwrap().val;
210
211 match file_i {
212 1 => {
213 opts.file1 = Some(val_str.clone());
214 }
215 2 => {
216 opts.file2 = Some(val_str.clone());
217 }
218 _ => {
219 return Err(ParseError::GenericErr(
220 "Invalid number of files".to_string(),
221 ));
222 }
223 }
224 file_i += 1;
225
226 if switch_mode {
227 Ok(ToggleParsingMode)
228 } else {
229 Ok(Continue)
230 }
231 }
232 },
233 )
234}
235
236pub fn main() -> Result<(), i32> {
237 let mut opts: Options = Default::default();
239
240 let rc = process_cmdopts(&mut opts);
241 if rc.is_ok() {
242 if std::env::args().len() <= 1 || opts.help.is_some() {
243 print_usage();
244 } else if opts.file1.is_none() {
245 eprintln!("[ERROR] FILE1 required");
246 return Err(2);
247 } else {
248 println!("{:?}", opts);
249 }
250 } else {
251 eprintln!("[ERROR] {}", rc.unwrap_err());
252 return Err(1);
253 }
254 Ok(())
255}