Skip to content

Commit 22f53a8

Browse files
committed
add support for repeated flags
If a flag is defined with 'mutiple()' - we collect the flag repeatly. I have extened the `Context` with functioin to fetch all occurence of a flag. E.g. `string_flag_vec` which return a Vector of `Result<String, FlagError>` similar with the other flag types.
1 parent b3282e4 commit 22f53a8

File tree

2 files changed

+184
-12
lines changed

2 files changed

+184
-12
lines changed

src/context.rs

Lines changed: 147 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,31 @@ impl Context {
2121
let flags_val = match flags {
2222
Some(flags) => {
2323
for flag in flags {
24-
if let Some(index) = flag.option_index(&parsed_args) {
25-
parsed_args.remove(index);
24+
let mut found_flag = false;
25+
loop {
26+
if let Some(index) = flag.option_index(&parsed_args) {
27+
found_flag = true;
28+
parsed_args.remove(index);
2629

27-
let val = if flag.flag_type != FlagType::Bool {
28-
if parsed_args.len() <= index {
29-
None
30+
let val = if flag.flag_type != FlagType::Bool {
31+
if parsed_args.len() <= index {
32+
None
33+
} else {
34+
Some(parsed_args.remove(index))
35+
}
3036
} else {
31-
Some(parsed_args.remove(index))
37+
None
38+
};
39+
v.push((flag.name.to_string(), flag.value(val)));
40+
if !flag.multiple {
41+
break;
3242
}
3343
} else {
34-
None
35-
};
36-
v.push((flag.name.to_string(), flag.value(val)))
37-
} else {
38-
v.push((flag.name.to_string(), Err(FlagError::NotFound)))
44+
if !found_flag {
45+
v.push((flag.name.to_string(), Err(FlagError::NotFound)));
46+
}
47+
break;
48+
}
3949
}
4050
}
4151
Some(v)
@@ -66,6 +76,15 @@ impl Context {
6676
}
6777
}
6878

79+
// Get flag values for repeated flags
80+
fn result_flag_value_vec(&self, name: &str) -> Vec::<Result<FlagValue, FlagError>> {
81+
self.flags.as_ref().unwrap().iter().filter(|flag| flag.0 == name)
82+
.map(|f| match &f.1 {
83+
Ok(val) => Ok(val.to_owned()),
84+
Err(e) => Err(e.to_owned()),
85+
}).collect::<Vec<_>>()
86+
}
87+
6988
/// Get bool flag
7089
///
7190
/// Example
@@ -111,6 +130,34 @@ impl Context {
111130
}
112131
}
113132

133+
/// Get string flags for repeated flags
134+
///
135+
/// Example
136+
///
137+
/// ```
138+
/// use seahorse::Context;
139+
///
140+
/// fn action(c: &Context) {
141+
/// for s in c.string_flag_vec("string") {
142+
/// match s {
143+
/// Ok(s) => println!("{}", s),
144+
/// Err(e) => println!("{}", e)
145+
/// }
146+
/// }
147+
/// }
148+
/// ```
149+
pub fn string_flag_vec(&self, name: &str) -> Vec<Result<String, FlagError>> {
150+
let r = self.result_flag_value_vec(name);
151+
152+
r.iter().map(|r|
153+
{
154+
match r {
155+
Ok(FlagValue::String(val)) => Ok(val.clone()),
156+
_ => Err(FlagError::TypeError),
157+
}
158+
}).collect::<Vec<_>>()
159+
}
160+
114161
/// Get int flag
115162
///
116163
/// Example
@@ -133,6 +180,34 @@ impl Context {
133180
}
134181
}
135182

183+
/// Get int flags for repeated flags
184+
///
185+
/// Example
186+
///
187+
/// ```
188+
/// use seahorse::Context;
189+
///
190+
/// fn action(c: &Context) {
191+
/// for i in c.int_flag_vec("int") {
192+
/// match i {
193+
/// Ok(i) => println!("{}", i),
194+
/// Err(e) => println!("{}", e)
195+
/// }
196+
/// }
197+
/// }
198+
/// ```
199+
pub fn int_flag_vec(&self, name: &str) -> Vec<Result<isize, FlagError>> {
200+
let r = self.result_flag_value_vec(name);
201+
202+
r.iter().map(|r|
203+
{
204+
match r {
205+
Ok(FlagValue::Int(val)) => Ok(val.clone()),
206+
_ => Err(FlagError::TypeError),
207+
}
208+
}).collect::<Vec<_>>()
209+
}
210+
136211
/// Get Uint flag
137212
///
138213
/// Example
@@ -155,6 +230,34 @@ impl Context {
155230
}
156231
}
157232

233+
/// Get uint flags for repeated flags
234+
///
235+
/// Example
236+
///
237+
/// ```
238+
/// use seahorse::Context;
239+
///
240+
/// fn action(c: &Context) {
241+
/// for i in c.uint_flag_vec("uint") {
242+
/// match i {
243+
/// Ok(i) => println!("{}", i),
244+
/// Err(e) => println!("{}", e)
245+
/// }
246+
/// }
247+
/// }
248+
/// ```
249+
pub fn uint_flag_vec(&self, name: &str) -> Vec<Result<usize, FlagError>> {
250+
let r = self.result_flag_value_vec(name);
251+
252+
r.iter().map(|r|
253+
{
254+
match r {
255+
Ok(FlagValue::Uint(val)) => Ok(val.clone()),
256+
_ => Err(FlagError::TypeError),
257+
}
258+
}).collect::<Vec<_>>()
259+
}
260+
158261
/// Get float flag
159262
///
160263
/// Example
@@ -177,6 +280,36 @@ impl Context {
177280
}
178281
}
179282

283+
/// Get float flags for repeated flags
284+
///
285+
/// Example
286+
///
287+
/// ```
288+
/// use seahorse::Context;
289+
///
290+
/// fn action(c: &Context) {
291+
/// for f in c.float_flag_vec("float") {
292+
/// match f {
293+
/// Ok(f) => println!("{}", f),
294+
/// Err(e) => println!("{}", e)
295+
/// }
296+
/// }
297+
/// }
298+
/// ```
299+
pub fn float_flag_vec(&self, name: &str) -> Vec<Result<f64, FlagError>> {
300+
let r = self.result_flag_value_vec(name);
301+
302+
// I would like to map the Result<FlagValue, FlagError> to Result<f64, FlagError>
303+
304+
r.iter().map(|r|
305+
{
306+
match *r {
307+
Ok(FlagValue::Float(val)) => Ok(val),
308+
_ => Err(FlagError::TypeError),
309+
}
310+
}).collect::<Vec<_>>()
311+
}
312+
180313
/// Display help
181314
///
182315
/// Example
@@ -213,6 +346,8 @@ mod tests {
213346
"1234567654321".to_string(),
214347
"--float".to_string(),
215348
"1.23".to_string(),
349+
"--float".to_string(),
350+
"1.44".to_string(),
216351
"--invalid_float".to_string(),
217352
"invalid".to_string(),
218353
];
@@ -221,7 +356,7 @@ mod tests {
221356
Flag::new("string", FlagType::String),
222357
Flag::new("int", FlagType::Int),
223358
Flag::new("uint", FlagType::Uint),
224-
Flag::new("float", FlagType::Float),
359+
Flag::new("float", FlagType::Float).multiple(),
225360
Flag::new("invalid_float", FlagType::Float),
226361
Flag::new("not_specified", FlagType::String),
227362
];

src/flag.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pub struct Flag {
1313
pub flag_type: FlagType,
1414
/// Flag alias
1515
pub alias: Option<Vec<String>>,
16+
/// Multiple occurrence
17+
pub multiple: bool,
1618
}
1719

1820
/// `FlagType` enum
@@ -72,6 +74,7 @@ impl Flag {
7274
description: None,
7375
flag_type,
7476
alias: None,
77+
multiple: false,
7578
}
7679
}
7780

@@ -90,6 +93,21 @@ impl Flag {
9093
self
9194
}
9295

96+
/// Set multiple flag
97+
///
98+
/// Example
99+
///
100+
/// ```
101+
/// use seahorse::{Flag, FlagType};
102+
///
103+
/// let bool_flag = Flag::new("bool", FlagType::Bool)
104+
/// .multiple();
105+
/// ```
106+
pub fn multiple(mut self) -> Self {
107+
self.multiple = true;
108+
self
109+
}
110+
93111
/// Set alias of the flag
94112
///
95113
/// Example
@@ -284,4 +302,23 @@ mod tests {
284302
_ => assert!(false),
285303
}
286304
}
305+
306+
#[test]
307+
fn multiple_string_flag_test() {
308+
let string_flag = Flag::new("string", FlagType::String);
309+
let v = vec![
310+
"cli".to_string(),
311+
"command".to_string(),
312+
"args".to_string(),
313+
"--string".to_string(),
314+
"test".to_string(),
315+
"--string".to_string(),
316+
"test2".to_string(),
317+
];
318+
319+
match string_flag.value(Some(v[4].to_owned())) {
320+
Ok(FlagValue::String(val)) => assert_eq!("test".to_string(), val),
321+
_ => assert!(false),
322+
}
323+
}
287324
}

0 commit comments

Comments
 (0)