@@ -28,6 +28,148 @@ fn arg_num_vals(arg: &clap::Arg) -> usize {
2828 }
2929}
3030
31+ /// Lookup tables mapping flag names to their argument definitions.
32+ ///
33+ /// Used to identify which flags belong to `UpCommandInner` vs. trigger-specific
34+ /// flags that should be passed through untouched.
35+ struct FlagIndex < ' a > {
36+ long : HashMap < & ' a str , & ' a clap:: Arg > ,
37+ short : HashMap < char , & ' a clap:: Arg > ,
38+ }
39+
40+ impl < ' a > FlagIndex < ' a > {
41+ fn from_command ( cmd : & ' a clap:: Command ) -> Self {
42+ let mut long = HashMap :: new ( ) ;
43+ let mut short = HashMap :: new ( ) ;
44+ for arg in cmd. get_arguments ( ) {
45+ for name in arg
46+ . get_long ( )
47+ . into_iter ( )
48+ . chain ( arg. get_all_aliases ( ) . unwrap_or_default ( ) )
49+ {
50+ long. insert ( name, arg) ;
51+ }
52+ for ch in arg
53+ . get_short ( )
54+ . into_iter ( )
55+ . chain ( arg. get_all_short_aliases ( ) . unwrap_or_default ( ) )
56+ {
57+ short. insert ( ch, arg) ;
58+ }
59+ }
60+ Self { long, short }
61+ }
62+ }
63+
64+ /// Consume the next `n` raw values from the cursor and append them to `dest`.
65+ fn take_values (
66+ dest : & mut Vec < OsString > ,
67+ raw_args : & clap_lex:: RawArgs ,
68+ cursor : & mut clap_lex:: ArgCursor ,
69+ n : usize ,
70+ ) {
71+ for _ in 0 ..n {
72+ dest. extend ( raw_args. next_os ( cursor) . map ( ToOwned :: to_owned) ) ;
73+ }
74+ }
75+
76+ /// Split raw arguments into those recognized by `UpCommandInner` ("up args")
77+ /// and everything else ("trigger args").
78+ ///
79+ /// Unrecognized arguments are kept as trigger args so that each trigger's
80+ /// own parser can handle them. If something truly invalid slips through,
81+ /// the later `try_parse_from` call will produce a clear clap error.
82+ fn split_args ( flags : & FlagIndex , raw_args : clap_lex:: RawArgs ) -> ( Vec < OsString > , Vec < OsString > ) {
83+ let mut up_args: Vec < OsString > = vec ! [ "spin up" . into( ) ] ;
84+ let mut trigger_args: Vec < OsString > = vec ! [ ] ;
85+ let mut cursor = raw_args. cursor ( ) ;
86+
87+ while let Some ( parsed) = raw_args. next ( & mut cursor) {
88+ if let Some ( ( Ok ( long) , eq_value) ) = parsed. to_long ( ) {
89+ classify_long_flag (
90+ long,
91+ eq_value,
92+ & parsed,
93+ flags,
94+ & raw_args,
95+ & mut cursor,
96+ & mut up_args,
97+ & mut trigger_args,
98+ ) ;
99+ } else if let Some ( shorts) = parsed. to_short ( ) {
100+ classify_short_flags (
101+ shorts,
102+ flags,
103+ & raw_args,
104+ & mut cursor,
105+ & mut up_args,
106+ & mut trigger_args,
107+ ) ;
108+ } else {
109+ // Not a flag — UpCommandInner has no positional args, so this
110+ // belongs to a trigger.
111+ trigger_args. push ( parsed. to_value_os ( ) . into ( ) ) ;
112+ }
113+ }
114+
115+ ( up_args, trigger_args)
116+ }
117+
118+ /// Route a `--long` flag (and its value(s)) to either up_args or trigger_args.
119+ #[ allow( clippy:: too_many_arguments) ]
120+ fn classify_long_flag (
121+ long : & str ,
122+ eq_value : Option < & OsStr > ,
123+ parsed : & clap_lex:: ParsedArg < ' _ > ,
124+ flags : & FlagIndex ,
125+ raw_args : & clap_lex:: RawArgs ,
126+ cursor : & mut clap_lex:: ArgCursor ,
127+ up_args : & mut Vec < OsString > ,
128+ trigger_args : & mut Vec < OsString > ,
129+ ) {
130+ let Some ( arg) = flags. long . get ( long) else {
131+ trigger_args. push ( parsed. to_value_os ( ) . into ( ) ) ;
132+ return ;
133+ } ;
134+
135+ up_args. push ( parsed. to_value_os ( ) . to_os_string ( ) ) ;
136+
137+ // Determine how many trailing values to consume. If the value was
138+ // provided inline (e.g. `--key=value`), one has already been consumed.
139+ let mut remaining = arg_num_vals ( arg) ;
140+ if remaining > 0 && eq_value. is_some ( ) {
141+ remaining -= 1 ;
142+ }
143+ take_values ( up_args, raw_args, cursor, remaining) ;
144+ }
145+
146+ /// Route each flag in a short-flag cluster (e.g. `-abc`) to up_args or
147+ /// trigger_args. Clap allows stacking, so `-abc` is equivalent to `-a -b -c`.
148+ fn classify_short_flags (
149+ shorts : clap_lex:: ShortFlags < ' _ > ,
150+ flags : & FlagIndex ,
151+ raw_args : & clap_lex:: RawArgs ,
152+ cursor : & mut clap_lex:: ArgCursor ,
153+ up_args : & mut Vec < OsString > ,
154+ trigger_args : & mut Vec < OsString > ,
155+ ) {
156+ for short_res in shorts {
157+ match short_res {
158+ Ok ( ch) if flags. short . contains_key ( & ch) => {
159+ up_args. push ( format ! ( "-{ch}" ) . into ( ) ) ;
160+ let remaining = arg_num_vals ( flags. short [ & ch] ) ;
161+ take_values ( up_args, raw_args, cursor, remaining) ;
162+ }
163+ Ok ( ch) => {
164+ trigger_args. push ( format ! ( "-{ch}" ) . into ( ) ) ;
165+ }
166+ Err ( invalid) => {
167+ trigger_args. push ( OsString :: from_iter ( [ OsStr :: new ( "-" ) , invalid] ) ) ;
168+ }
169+ }
170+ }
171+ }
172+
31173impl Args for UpCommand {
32174 fn augment_args ( cmd : clap:: Command ) -> clap:: Command {
33175 // The FromArgMatches impl below depends on some restrictions on
@@ -61,81 +203,10 @@ impl Args for UpCommand {
61203impl clap:: FromArgMatches for UpCommand {
62204 fn from_arg_matches ( matches : & clap:: ArgMatches ) -> std:: result:: Result < Self , clap:: Error > {
63205 let cmd = UpCommandInner :: command ( ) ;
206+ let flags = FlagIndex :: from_command ( & cmd) ;
64207
65- // Build our own maps of flags -> arguments, because clap doesn't like to share
66- let mut long_flags = HashMap :: new ( ) ;
67- let mut short_flags = HashMap :: new ( ) ;
68- for arg in cmd. get_arguments ( ) {
69- for long in arg
70- . get_long ( )
71- . into_iter ( )
72- . chain ( arg. get_all_aliases ( ) . unwrap_or_default ( ) )
73- {
74- long_flags. insert ( long, arg) ;
75- }
76- for short in arg
77- . get_short ( )
78- . into_iter ( )
79- . chain ( arg. get_all_short_aliases ( ) . unwrap_or_default ( ) )
80- {
81- short_flags. insert ( short, arg) ;
82- }
83- }
84-
85- // Use clap's own lexer against it
86208 let raw_args = clap_lex:: RawArgs :: new ( matches. get_raw ( "args" ) . into_iter ( ) . flatten ( ) ) ;
87- let mut cursor = raw_args. cursor ( ) ;
88-
89- // Split the arguments into those recognized by UpInnerCommand and all
90- // others. As a general strategy if we see something unexpected at this
91- // stage we let it through with the expectation that later parsing will
92- // catch it and produce a nice clap error.
93- let mut up_args = vec ! [ "spin up" . into( ) ] ;
94- let mut trigger_args = vec ! [ ] ;
95- while let Some ( parsed) = raw_args. next ( & mut cursor) {
96- // --long flags
97- if let Some ( ( Ok ( long) , eq_value) ) = parsed. to_long ( ) {
98- if let Some ( arg) = long_flags. get ( long) {
99- up_args. push ( parsed. to_value_os ( ) . to_os_string ( ) ) ;
100- // We asserted earlier that there are a fixed number of values
101- let mut need_args = arg_num_vals ( arg) ;
102- // For --key=value args `value` needs to be counted
103- if need_args > 0 && eq_value. is_some ( ) {
104- need_args -= 1 ;
105- }
106- // Grab any required value(s)
107- for _ in 0 ..need_args {
108- up_args. extend ( raw_args. next_os ( & mut cursor) . map ( ToOwned :: to_owned) ) ;
109- }
110- } else {
111- trigger_args. push ( parsed. to_value_os ( ) . into ( ) ) ;
112- }
113- // -short flags; note that clap allows "stacked" flags, i.e. `-abc` == `-a -b -c`
114- } else if let Some ( shorts) = parsed. to_short ( ) {
115- for short_res in shorts {
116- match short_res {
117- Ok ( short) if short_flags. contains_key ( & short) => {
118- up_args. push ( format ! ( "-{short}" ) . into ( ) ) ;
119- let arg = short_flags[ & short] ;
120- let need_args = arg_num_vals ( arg) ;
121- for _ in 0 ..need_args {
122- up_args
123- . extend ( raw_args. next_os ( & mut cursor) . map ( ToOwned :: to_owned) ) ;
124- }
125- }
126- Ok ( short) => {
127- trigger_args. push ( format ! ( "-{short}" ) . into ( ) ) ;
128- }
129- Err ( invalid) => {
130- trigger_args. push ( OsString :: from_iter ( [ OsStr :: new ( "-" ) , invalid] ) ) ;
131- }
132- }
133- }
134- } else {
135- // We asserted that UpCommandInner has no positional args
136- trigger_args. push ( parsed. to_value_os ( ) . into ( ) ) ;
137- }
138- }
209+ let ( up_args, trigger_args) = split_args ( & flags, raw_args) ;
139210
140211 let mut inner = UpCommandInner :: try_parse_from ( up_args) ?;
141212 inner. trigger_args = trigger_args;
0 commit comments