@@ -83,33 +83,50 @@ 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 {
9193 let mut args = args. iter ( ) ;
9294
93- // The case we are concerned with is where we have `<tool> <number>`.
94- // This is only interesting if there are exactly two args. Then we care
95- // whether the two items are a bare name (with no `@version`), followed
96- // by a valid version specifier (ignoring custom tags). That is:
97- //
98- // - `volta install node@lts latest` is allowed.
99- // - `volta install node latest` is an error.
100- // - `volta install node latest yarn` is allowed.
101- if let ( Some ( name) , Some ( maybe_version) , None ) = ( args. next ( ) , args. next ( ) , args. next ( ) ) {
102- if !HAS_VERSION . is_match ( name. as_ref ( ) ) && is_version_like ( maybe_version. as_ref ( ) ) {
103- return Err ( ErrorKind :: InvalidInvocation {
95+ match ( args. next ( ) , args. next ( ) , args. next ( ) ) {
96+ // The case we are concerned with here is where we have `<number>`.
97+ // That is, exactly one argument, which is a valid version specifier.
98+ //
99+ // - `volta install node@12` is allowed.
100+ // - `volta install 12` is an error.
101+ // - `volta install lts` is an error.
102+ ( Some ( maybe_version) , None , None ) if is_version_like ( maybe_version. as_ref ( ) ) => {
103+ Err ( ErrorKind :: InvalidInvocationOfBareVersion {
104+ action : action. to_string ( ) ,
105+ version : maybe_version. as_ref ( ) . to_string ( ) ,
106+ }
107+ . into ( ) )
108+ }
109+ // The case we are concerned with here is where we have `<tool> <number>`.
110+ // This is only interesting if there are exactly two args. Then we care
111+ // whether the two items are a bare name (with no `@version`), followed
112+ // by a valid version specifier (ignoring custom tags). That is:
113+ //
114+ // - `volta install node@lts latest` is allowed.
115+ // - `volta install node latest` is an error.
116+ // - `volta install node latest yarn` is allowed.
117+ ( Some ( name) , Some ( maybe_version) , None )
118+ if !HAS_VERSION . is_match ( name. as_ref ( ) )
119+ && is_version_like ( maybe_version. as_ref ( ) ) =>
120+ {
121+ Err ( ErrorKind :: InvalidInvocation {
104122 action : action. to_string ( ) ,
105123 name : name. as_ref ( ) . to_string ( ) ,
106124 version : maybe_version. as_ref ( ) . to_string ( ) ,
107125 }
108- . into ( ) ) ;
126+ . into ( ) )
109127 }
128+ _ => Ok ( ( ) ) ,
110129 }
111-
112- Ok ( ( ) )
113130 }
114131
115132 /// Compare `Spec`s for sorting when converting from strings
@@ -358,6 +375,23 @@ mod tests {
358375
359376 static PIN : & str = "pin" ;
360377
378+ #[ test]
379+ fn special_cases_just_number ( ) {
380+ let version = "1.2.3" ;
381+ let args: Vec < String > = vec ! [ version. into( ) ] ;
382+
383+ let err = Spec :: from_strings ( & args, PIN ) . unwrap_err ( ) ;
384+
385+ assert_eq ! (
386+ err. kind( ) ,
387+ & ErrorKind :: InvalidInvocationOfBareVersion {
388+ action: PIN . into( ) ,
389+ version: version. into( )
390+ } ,
391+ "`volta <action> number` results in the correct error"
392+ ) ;
393+ }
394+
361395 #[ test]
362396 fn special_cases_tool_space_number ( ) {
363397 let name = "potato" ;
@@ -393,6 +427,15 @@ mod tests {
393427 "when there is only one arg"
394428 ) ;
395429
430+ let one_with_explicit_verson = [ "10@latest" . to_owned ( ) ] ;
431+ assert_eq ! (
432+ Spec :: from_strings( & one_with_explicit_verson, PIN )
433+ . expect( "is ok" )
434+ . len( ) ,
435+ only_one. len( ) ,
436+ "when the sole arg is version-like but has an explicit version"
437+ ) ;
438+
396439 let two_but_unmistakable = [ "12" . to_owned ( ) , "node" . to_owned ( ) ] ;
397440 assert_eq ! (
398441 Spec :: from_strings( & two_but_unmistakable, PIN )
0 commit comments