@@ -6,9 +6,14 @@ type ItemCallbackFn<T> = fn(menu: &Menu<T>, item: &Item<T>, args: &[&str], conte
66#[ derive( Debug ) ]
77/// Describes a parameter to the command
88pub enum Parameter < ' a > {
9+ /// A mandatory positional parameter
910 Mandatory ( & ' a str ) ,
11+ /// An optional positional parameter. Must come after the mandatory positional arguments.
1012 Optional ( & ' a str ) ,
11- Named {
13+ /// A named parameter with no argument (e.g. `--verbose` or `--dry-run`)
14+ Named ( & ' a str ) ,
15+ /// A named parameter with argument (e.g. `--mode=foo` or `--level=3`)
16+ NamedValue {
1217 parameter_name : & ' a str ,
1318 argument_name : & ' a str ,
1419 } ,
@@ -62,6 +67,104 @@ where
6267 pub context : & ' a mut T ,
6368}
6469
70+ /// Looks for the named parameter in the parameter list of the item, then
71+ /// finds the correct argument.
72+ ///
73+ /// * Returns `Ok(None)` if `parameter_name` gives an optional or named
74+ /// parameter and that argument was not given.
75+ /// * Returns `Ok(arg)` if the argument corresponding to `parameter_name` was
76+ /// found. `arg` is the empty string if the parameter was `Parameter::Named`
77+ /// (and hence doesn't take a value).
78+ /// * Returns `Err(())` if `parameter_name` was not in `item.parameter_list`
79+ /// or `item` wasn't an Item::Callback
80+ pub fn argument_finder < ' a , T > (
81+ item : & ' a Item < ' a , T > ,
82+ argument_list : & ' a [ & ' a str ] ,
83+ name_to_find : & ' a str ,
84+ ) -> Result < Option < & ' a str > , ( ) > {
85+ if let ItemType :: Callback { parameters, .. } = item. item_type {
86+ // Step 1 - Find `name_to_find` in the parameter list.
87+ let mut found_param = None ;
88+ let mut mandatory_count = 0 ;
89+ let mut optional_count = 0 ;
90+ for param in parameters. iter ( ) {
91+ match param {
92+ Parameter :: Mandatory ( name) => {
93+ mandatory_count += 1 ;
94+ if * name == name_to_find {
95+ found_param = Some ( ( param, mandatory_count) ) ;
96+ }
97+ }
98+ Parameter :: Optional ( name) => {
99+ optional_count += 1 ;
100+ if * name == name_to_find {
101+ found_param = Some ( ( param, optional_count) ) ;
102+ }
103+ }
104+ Parameter :: Named ( name) => {
105+ if * name == name_to_find {
106+ found_param = Some ( ( param, 0 ) ) ;
107+ }
108+ }
109+ _ => {
110+ unimplemented ! ( ) ;
111+ }
112+ }
113+ }
114+ // Step 2 - What sort of parameter is it?
115+ match found_param {
116+ // Step 2a - Mandatory Positional
117+ Some ( ( Parameter :: Mandatory ( _name) , mandatory_idx) ) => {
118+ // We want positional parameter number `mandatory_idx` of `mandatory_count`.
119+ let mut positional_args_seen = 0 ;
120+ for arg in argument_list {
121+ if !arg. starts_with ( "--" ) {
122+ // Positional
123+ positional_args_seen += 1 ;
124+ if positional_args_seen == mandatory_idx {
125+ return Ok ( Some ( arg) ) ;
126+ }
127+ }
128+ }
129+ // Valid thing to ask for but we don't have it
130+ Ok ( None )
131+ }
132+ // Step 2b - Optional Positional
133+ Some ( ( Parameter :: Optional ( _name) , optional_idx) ) => {
134+ // We want positional parameter number `mandatory_idx` of `mandatory_count`.
135+ let mut positional_args_seen = 0 ;
136+ for arg in argument_list {
137+ if !arg. starts_with ( "--" ) {
138+ // Positional
139+ positional_args_seen += 1 ;
140+ if positional_args_seen == ( mandatory_count + optional_idx) {
141+ return Ok ( Some ( arg) ) ;
142+ }
143+ }
144+ }
145+ // Valid thing to ask for but we don't have it
146+ Ok ( None )
147+ }
148+ // Step 2c - Named
149+ Some ( ( Parameter :: Named ( name) , _) ) => {
150+ for arg in argument_list {
151+ if arg. starts_with ( "--" ) && ( & arg[ 2 ..] == * name) {
152+ return Ok ( Some ( "" ) ) ;
153+ }
154+ }
155+ // Valid thing to ask for but we don't have it
156+ Ok ( None )
157+ }
158+ // Step 2d - NamedValue
159+ // Step 2e - not found
160+ _ => Err ( ( ) ) ,
161+ }
162+ } else {
163+ // Not an item with arguments
164+ Err ( ( ) )
165+ }
166+ }
167+
65168enum Outcome {
66169 CommandProcessed ,
67170 NeedMore ,
@@ -239,7 +342,10 @@ where
239342 Parameter :: Optional ( name) => {
240343 write ! ( self . context, " [ <{}> ]" , name) . unwrap ( ) ;
241344 }
242- Parameter :: Named {
345+ Parameter :: Named ( name) => {
346+ write ! ( self . context, " [ --{} ]" , name) . unwrap ( ) ;
347+ }
348+ Parameter :: NamedValue {
243349 parameter_name,
244350 argument_name,
245351 } => {
@@ -280,6 +386,14 @@ where
280386 _ => false ,
281387 } )
282388 . count ( ) ;
389+ let positional_parameter_count = parameters
390+ . iter ( )
391+ . filter ( |p| match p {
392+ Parameter :: Mandatory ( _) => true ,
393+ Parameter :: Optional ( _) => true ,
394+ _ => false ,
395+ } )
396+ . count ( ) ;
283397 if command. len ( ) >= item. command . len ( ) {
284398 // Maybe arguments
285399 let mut argument_buffer: [ & str ; 16 ] = [ "" ; 16 ] ;
@@ -291,19 +405,49 @@ where
291405 {
292406 * slot = arg;
293407 argument_count += 1 ;
294- if !arg. starts_with ( "--" ) {
408+ if arg. starts_with ( "--" ) {
409+ // Validate named argument
410+ let mut found = false ;
411+ for param in parameters. iter ( ) {
412+ match param {
413+ Parameter :: Named ( name) => {
414+ if & arg[ 2 ..] == * name {
415+ found = true ;
416+ break ;
417+ }
418+ }
419+ Parameter :: NamedValue { parameter_name, .. } => {
420+ if let Some ( name) = arg[ 2 ..] . split ( '=' ) . next ( ) {
421+ if name == * parameter_name {
422+ found = true ;
423+ break ;
424+ }
425+ }
426+ }
427+ _ => {
428+ // Ignore
429+ }
430+ }
431+ }
432+ if !found {
433+ writeln ! ( context, "Error: Did not understand {:?}" , arg) . unwrap ( ) ;
434+ return ;
435+ }
436+ } else {
295437 positional_arguments += 1 ;
296438 }
297439 }
298- if positional_arguments >= mandatory_parameter_count {
440+ if positional_arguments < mandatory_parameter_count {
441+ writeln ! ( context, "Error: Insufficient arguments given" ) . unwrap ( ) ;
442+ } else if positional_arguments > positional_parameter_count {
443+ writeln ! ( context, "Error: Too many arguments given" ) . unwrap ( ) ;
444+ } else {
299445 callback_function (
300446 parent_menu,
301447 item,
302448 & argument_buffer[ 0 ..argument_count] ,
303449 context,
304450 ) ;
305- } else {
306- writeln ! ( context, "Error: Insufficient arguments given" ) . unwrap ( ) ;
307451 }
308452 } else {
309453 // Definitely no arguments
@@ -318,8 +462,104 @@ where
318462
319463#[ cfg( test) ]
320464mod tests {
465+ use super :: * ;
466+
467+ fn dummy ( _menu : & Menu < u32 > , _item : & Item < u32 > , _args : & [ & str ] , _context : & mut u32 ) { }
468+
469+ #[ test]
470+ fn find_arg_mandatory ( ) {
471+ let item = Item {
472+ command : "dummy" ,
473+ help : None ,
474+ item_type : ItemType :: Callback {
475+ function : dummy,
476+ parameters : & [
477+ Parameter :: Mandatory ( "foo" ) ,
478+ Parameter :: Mandatory ( "bar" ) ,
479+ Parameter :: Mandatory ( "baz" ) ,
480+ ] ,
481+ } ,
482+ } ;
483+ assert_eq ! (
484+ argument_finder( & item, & [ "a" , "b" , "c" ] , "foo" ) ,
485+ Ok ( Some ( "a" ) )
486+ ) ;
487+ assert_eq ! (
488+ argument_finder( & item, & [ "a" , "b" , "c" ] , "bar" ) ,
489+ Ok ( Some ( "b" ) )
490+ ) ;
491+ assert_eq ! (
492+ argument_finder( & item, & [ "a" , "b" , "c" ] , "baz" ) ,
493+ Ok ( Some ( "c" ) )
494+ ) ;
495+ // Not an argument
496+ assert_eq ! ( argument_finder( & item, & [ "a" , "b" , "c" ] , "quux" ) , Err ( ( ) ) ) ;
497+ }
498+
321499 #[ test]
322- fn it_works ( ) {
323- assert_eq ! ( 2 + 2 , 4 ) ;
500+ fn find_arg_optional ( ) {
501+ let item = Item {
502+ command : "dummy" ,
503+ help : None ,
504+ item_type : ItemType :: Callback {
505+ function : dummy,
506+ parameters : & [
507+ Parameter :: Mandatory ( "foo" ) ,
508+ Parameter :: Mandatory ( "bar" ) ,
509+ Parameter :: Optional ( "baz" ) ,
510+ ] ,
511+ } ,
512+ } ;
513+ assert_eq ! (
514+ argument_finder( & item, & [ "a" , "b" , "c" ] , "foo" ) ,
515+ Ok ( Some ( "a" ) )
516+ ) ;
517+ assert_eq ! (
518+ argument_finder( & item, & [ "a" , "b" , "c" ] , "bar" ) ,
519+ Ok ( Some ( "b" ) )
520+ ) ;
521+ assert_eq ! (
522+ argument_finder( & item, & [ "a" , "b" , "c" ] , "baz" ) ,
523+ Ok ( Some ( "c" ) )
524+ ) ;
525+ // Not an argument
526+ assert_eq ! ( argument_finder( & item, & [ "a" , "b" , "c" ] , "quux" ) , Err ( ( ) ) ) ;
527+ // Missing optional
528+ assert_eq ! ( argument_finder( & item, & [ "a" , "b" ] , "baz" ) , Ok ( None ) ) ;
529+ }
530+
531+ #[ test]
532+ fn find_arg_named ( ) {
533+ let item = Item {
534+ command : "dummy" ,
535+ help : None ,
536+ item_type : ItemType :: Callback {
537+ function : dummy,
538+ parameters : & [
539+ Parameter :: Mandatory ( "foo" ) ,
540+ Parameter :: Named ( "bar" ) ,
541+ Parameter :: Named ( "baz" ) ,
542+ ] ,
543+ } ,
544+ } ;
545+ assert_eq ! (
546+ argument_finder( & item, & [ "a" , "--bar" , "--baz" ] , "foo" ) ,
547+ Ok ( Some ( "a" ) )
548+ ) ;
549+ assert_eq ! (
550+ argument_finder( & item, & [ "a" , "--bar" , "--baz" ] , "bar" ) ,
551+ Ok ( Some ( "" ) )
552+ ) ;
553+ assert_eq ! (
554+ argument_finder( & item, & [ "a" , "--bar" , "--baz" ] , "baz" ) ,
555+ Ok ( Some ( "" ) )
556+ ) ;
557+ // Not an argument
558+ assert_eq ! (
559+ argument_finder( & item, & [ "a" , "--bar" , "--baz" ] , "quux" ) ,
560+ Err ( ( ) )
561+ ) ;
562+ // Missing named
563+ assert_eq ! ( argument_finder( & item, & [ "a" ] , "baz" ) , Ok ( None ) ) ;
324564 }
325565}
0 commit comments