@@ -83,22 +83,44 @@ impl Spec {
8383 Ok ( tools)
8484 }
8585
86- /// Check the args for the bad pattern of `volta install <tool> <number>`.
86+ /// Check the args for the bad patterns of
87+ /// - `volta install <number>`
88+ /// - `volta install <tool> <number>`
8789 fn check_args < T > ( args : & [ T ] , action : & str ) -> Fallible < ( ) >
8890 where
8991 T : AsRef < str > ,
9092 {
91- let mut args = args. iter ( ) ;
93+ let mut args_iter = args. iter ( ) ;
9294
93- // The case we are concerned with is where we have `<tool> <number>`.
95+ // The case we are concerned with here is where we have `<number>`.
96+ // That is, exactly one argument, which is a valid version specifier.
97+ //
98+ // - `volta install node@12` is allowed.
99+ // - `volta install 12` is an error.
100+ // - `volta install lts` is an error.
101+ if let ( Some ( maybe_version) , None ) = ( args_iter. next ( ) , args_iter. next ( ) ) {
102+ if is_version_like ( maybe_version. as_ref ( ) ) {
103+ return Err ( ErrorKind :: InvalidInvocationOfBareVersion {
104+ action : action. to_string ( ) ,
105+ version : maybe_version. as_ref ( ) . to_string ( ) ,
106+ }
107+ . into ( ) ) ;
108+ }
109+ }
110+
111+ args_iter = args. iter ( ) ;
112+
113+ // The case we are concerned with here is where we have `<tool> <number>`.
94114 // This is only interesting if there are exactly two args. Then we care
95115 // whether the two items are a bare name (with no `@version`), followed
96116 // by a valid version specifier (ignoring custom tags). That is:
97117 //
98118 // - `volta install node@lts latest` is allowed.
99119 // - `volta install node latest` is an error.
100120 // - `volta install node latest yarn` is allowed.
101- if let ( Some ( name) , Some ( maybe_version) , None ) = ( args. next ( ) , args. next ( ) , args. next ( ) ) {
121+ if let ( Some ( name) , Some ( maybe_version) , None ) =
122+ ( args_iter. next ( ) , args_iter. next ( ) , args_iter. next ( ) )
123+ {
102124 if !HAS_VERSION . is_match ( name. as_ref ( ) ) && is_version_like ( maybe_version. as_ref ( ) ) {
103125 return Err ( ErrorKind :: InvalidInvocation {
104126 action : action. to_string ( ) ,
@@ -358,6 +380,23 @@ mod tests {
358380
359381 static PIN : & str = "pin" ;
360382
383+ #[ test]
384+ fn special_cases_just_number ( ) {
385+ let version = "1.2.3" ;
386+ let args: Vec < String > = vec ! [ version. into( ) ] ;
387+
388+ let err = Spec :: from_strings ( & args, PIN ) . unwrap_err ( ) ;
389+
390+ assert_eq ! (
391+ err. kind( ) ,
392+ & ErrorKind :: InvalidInvocationOfBareVersion {
393+ action: PIN . into( ) ,
394+ version: version. into( )
395+ } ,
396+ "`volta <action> number` results in the correct error"
397+ ) ;
398+ }
399+
361400 #[ test]
362401 fn special_cases_tool_space_number ( ) {
363402 let name = "potato" ;
@@ -393,6 +432,15 @@ mod tests {
393432 "when there is only one arg"
394433 ) ;
395434
435+ let one_with_explicit_verson = [ "10@latest" . to_owned ( ) ] ;
436+ assert_eq ! (
437+ Spec :: from_strings( & one_with_explicit_verson, PIN )
438+ . expect( "is ok" )
439+ . len( ) ,
440+ only_one. len( ) ,
441+ "when the sole arg is version-like but has an explicit version"
442+ ) ;
443+
396444 let two_but_unmistakable = [ "12" . to_owned ( ) , "node" . to_owned ( ) ] ;
397445 assert_eq ! (
398446 Spec :: from_strings( & two_but_unmistakable, PIN )
0 commit comments