@@ -173,66 +173,26 @@ impl<'src> Justfile<'src> {
173173 _ => { }
174174 }
175175
176- let mut remaining: Vec < & str > = if !arguments. is_empty ( ) {
177- arguments. iter ( ) . map ( String :: as_str) . collect ( )
178- } else if let Some ( recipe) = & self . default {
179- recipe. check_can_be_default_recipe ( ) ?;
180- vec ! [ recipe. name( ) ]
181- } else if self . recipes . is_empty ( ) {
182- return Err ( Error :: NoRecipes ) ;
183- } else {
184- return Err ( Error :: NoDefaultRecipe ) ;
185- } ;
176+ let arguments = arguments. iter ( ) . map ( String :: as_str) . collect :: < Vec < & str > > ( ) ;
186177
187- let mut missing = Vec :: new ( ) ;
188- let mut invocations = Vec :: new ( ) ;
189- let mut scopes = BTreeMap :: new ( ) ;
190- let arena: Arena < Scope > = Arena :: new ( ) ;
191-
192- while let Some ( first) = remaining. first ( ) . copied ( ) {
193- if first. contains ( "::" )
194- && !( first. starts_with ( ':' ) || first. ends_with ( ':' ) || first. contains ( ":::" ) )
195- {
196- remaining = first
197- . split ( "::" )
198- . chain ( remaining[ 1 ..] . iter ( ) . copied ( ) )
199- . collect ( ) ;
200-
201- continue ;
202- }
178+ let groups = ArgumentParser :: parse_arguments ( self , & arguments) ?;
203179
204- let rest = & remaining[ 1 ..] ;
180+ let arena: Arena < Scope > = Arena :: new ( ) ;
181+ let mut invocations = Vec :: < Invocation > :: new ( ) ;
182+ let mut scopes = BTreeMap :: new ( ) ;
205183
206- if let Some ( ( invocation, consumed) ) = self . invocation (
207- 0 ,
208- & mut Vec :: new ( ) ,
184+ for group in & groups {
185+ invocations. push ( self . invocation (
209186 & arena,
210- & mut scopes ,
187+ & group . arguments ,
211188 config,
212189 & dotenv,
213- search,
214190 & scope,
215- first,
216- rest,
217- ) ? {
218- remaining = rest[ consumed..] . to_vec ( ) ;
219- invocations. push ( invocation) ;
220- } else {
221- missing. push ( first. to_string ( ) ) ;
222- remaining = rest. to_vec ( ) ;
223- }
224- }
225-
226- if !missing. is_empty ( ) {
227- let suggestion = if missing. len ( ) == 1 {
228- self . suggest_recipe ( missing. first ( ) . unwrap ( ) )
229- } else {
230- None
231- } ;
232- return Err ( Error :: UnknownRecipes {
233- recipes : missing,
234- suggestion,
235- } ) ;
191+ & group. path ,
192+ 0 ,
193+ & mut scopes,
194+ search,
195+ ) ?) ;
236196 }
237197
238198 let mut ran = Ran :: default ( ) ;
@@ -278,21 +238,29 @@ impl<'src> Justfile<'src> {
278238
279239 fn invocation < ' run > (
280240 & ' run self ,
281- depth : usize ,
282- path : & mut Vec < & ' run str > ,
283241 arena : & ' run Arena < Scope < ' src , ' run > > ,
284- scopes : & mut BTreeMap < Vec < & ' run str > , & ' run Scope < ' src , ' run > > ,
242+ arguments : & [ & ' run str ] ,
285243 config : & ' run Config ,
286244 dotenv : & ' run BTreeMap < String , String > ,
287- search : & ' run Search ,
288245 parent : & ' run Scope < ' src , ' run > ,
289- first : & ' run str ,
290- rest : & [ & ' run str ] ,
291- ) -> RunResult < ' src , Option < ( Invocation < ' src , ' run > , usize ) > > {
292- if let Some ( module) = self . modules . get ( first) {
293- path. push ( first) ;
246+ path : & ' run [ String ] ,
247+ position : usize ,
248+ scopes : & mut BTreeMap < & ' run [ String ] , & ' run Scope < ' src , ' run > > ,
249+ search : & ' run Search ,
250+ ) -> RunResult < ' src , Invocation < ' src , ' run > > {
251+ if position + 1 == path. len ( ) {
252+ let recipe = self . get_recipe ( & path[ position] ) . unwrap ( ) ;
253+ Ok ( Invocation {
254+ recipe,
255+ module_source : & self . source ,
256+ arguments : arguments. into ( ) ,
257+ settings : & self . settings ,
258+ scope : parent,
259+ } )
260+ } else {
261+ let module = self . modules . get ( & path[ position] ) . unwrap ( ) ;
294262
295- let scope = if let Some ( scope) = scopes. get ( path) {
263+ let scope = if let Some ( scope) = scopes. get ( & path[ ..position ] ) {
296264 scope
297265 } else {
298266 let scope = Evaluator :: evaluate_assignments (
@@ -304,76 +272,21 @@ impl<'src> Justfile<'src> {
304272 search,
305273 ) ?;
306274 let scope = arena. alloc ( scope) ;
307- scopes. insert ( path. clone ( ) , scope) ;
275+ scopes. insert ( path, scope) ;
308276 scopes. get ( path) . unwrap ( )
309277 } ;
310278
311- if rest. is_empty ( ) {
312- if let Some ( recipe) = & module. default {
313- recipe. check_can_be_default_recipe ( ) ?;
314- return Ok ( Some ( (
315- Invocation {
316- settings : & module. settings ,
317- recipe,
318- arguments : Vec :: new ( ) ,
319- scope,
320- module_source : & self . source ,
321- } ,
322- depth,
323- ) ) ) ;
324- }
325- Err ( Error :: NoDefaultRecipe )
326- } else {
327- module. invocation (
328- depth + 1 ,
329- path,
330- arena,
331- scopes,
332- config,
333- dotenv,
334- search,
335- scope,
336- rest[ 0 ] ,
337- & rest[ 1 ..] ,
338- )
339- }
340- } else if let Some ( recipe) = self . get_recipe ( first) {
341- if recipe. parameters . is_empty ( ) {
342- Ok ( Some ( (
343- Invocation {
344- arguments : Vec :: new ( ) ,
345- recipe,
346- scope : parent,
347- settings : & self . settings ,
348- module_source : & self . source ,
349- } ,
350- depth,
351- ) ) )
352- } else {
353- let argument_range = recipe. argument_range ( ) ;
354- let argument_count = cmp:: min ( rest. len ( ) , recipe. max_arguments ( ) ) ;
355- if !argument_range. range_contains ( & argument_count) {
356- return Err ( Error :: ArgumentCountMismatch {
357- recipe : recipe. name ( ) ,
358- parameters : recipe. parameters . clone ( ) ,
359- found : rest. len ( ) ,
360- min : recipe. min_arguments ( ) ,
361- max : recipe. max_arguments ( ) ,
362- } ) ;
363- }
364- Ok ( Some ( (
365- Invocation {
366- arguments : rest[ ..argument_count] . to_vec ( ) ,
367- recipe,
368- scope : parent,
369- settings : & self . settings ,
370- module_source : & self . source ,
371- } ,
372- depth + argument_count,
373- ) ) )
374- }
375- } else {
376- Ok ( None )
279+ module. invocation (
280+ arena,
281+ arguments,
282+ config,
283+ dotenv,
284+ scope,
285+ path,
286+ position + 1 ,
287+ scopes,
288+ search,
289+ )
377290 }
378291 }
379292
@@ -523,34 +436,51 @@ mod tests {
523436 use Error :: * ;
524437
525438 run_error ! {
526- name: unknown_recipes ,
439+ name: unknown_recipe_no_suggestion ,
527440 src: "a:\n b:\n c:" ,
528- args: [ "a" , "x " , "y" , "z" ] ,
529- error: UnknownRecipes {
530- recipes ,
441+ args: [ "a" , "xyz " , "y" , "z" ] ,
442+ error: UnknownRecipe {
443+ recipe ,
531444 suggestion,
532445 } ,
533446 check: {
534- assert_eq!( recipes , & [ "x" , "y" , "z" ] ) ;
447+ assert_eq!( recipe , "xyz" ) ;
535448 assert_eq!( suggestion, None ) ;
536449 }
537450 }
538451
539452 run_error ! {
540- name: unknown_recipes_show_alias_suggestion,
453+ name: unknown_recipe_with_suggestion,
454+ src: "a:\n b:\n c:" ,
455+ args: [ "a" , "x" , "y" , "z" ] ,
456+ error: UnknownRecipe {
457+ recipe,
458+ suggestion,
459+ } ,
460+ check: {
461+ assert_eq!( recipe, "x" ) ;
462+ assert_eq!( suggestion, Some ( Suggestion {
463+ name: "a" ,
464+ target: None ,
465+ } ) ) ;
466+ }
467+ }
468+
469+ run_error ! {
470+ name: unknown_recipe_show_alias_suggestion,
541471 src: "
542472 foo:
543473 echo foo
544474
545475 alias z := foo
546476 " ,
547477 args: [ "zz" ] ,
548- error: UnknownRecipes {
549- recipes ,
478+ error: UnknownRecipe {
479+ recipe ,
550480 suggestion,
551481 } ,
552482 check: {
553- assert_eq!( recipes , & [ "zz" ] ) ;
483+ assert_eq!( recipe , "zz" ) ;
554484 assert_eq!( suggestion, Some ( Suggestion {
555485 name: "z" ,
556486 target: Some ( "foo" ) ,
0 commit comments