@@ -116,13 +116,11 @@ impl RustInstaller {
116116 }
117117
118118 async fn find_installed ( & self , request : & RustRequest ) -> Result < RustResult > {
119- let toolchains: Vec < ToolchainInfo > = self . rustup . list_installed_toolchains ( ) . await ?;
119+ let mut toolchains: Vec < ToolchainInfo > = self . rustup . list_installed_toolchains ( ) . await ?;
120120
121- let installed = toolchains
122- . into_iter ( )
123- . sorted_unstable_by ( |a, b| b. version . cmp ( & a. version ) ) ;
121+ sort_toolchains ( & mut toolchains) ;
124122
125- installed
123+ toolchains
126124 . into_iter ( )
127125 . find_map ( |info| {
128126 let matches = request. matches ( & info. version , Some ( & info. path ) ) ;
@@ -139,13 +137,11 @@ impl RustInstaller {
139137 }
140138
141139 async fn find_system_rust ( & self , rust_request : & RustRequest ) -> Result < Option < RustResult > > {
142- let toolchains: Vec < ToolchainInfo > = self . rustup . list_system_toolchains ( ) . await ?;
140+ let mut toolchains: Vec < ToolchainInfo > = self . rustup . list_system_toolchains ( ) . await ?;
143141
144- let installed = toolchains
145- . into_iter ( )
146- . sorted_unstable_by ( |a, b| b. version . cmp ( & a. version ) ) ;
142+ sort_toolchains ( & mut toolchains) ;
147143
148- for info in installed {
144+ for info in toolchains {
149145 let matches = rust_request. matches ( & info. version , Some ( & info. path ) ) ;
150146
151147 if matches {
@@ -214,3 +210,77 @@ impl RustInstaller {
214210 Ok ( rust)
215211 }
216212}
213+
214+ fn sort_toolchains ( toolchains : & mut [ ToolchainInfo ] ) {
215+ fn channel_preference ( version : & RustVersion ) -> u8 {
216+ match version. channel ( ) {
217+ Some ( Channel :: Nightly ) => 2 ,
218+ Some ( Channel :: Beta ) => 1 ,
219+ // Exact release toolchains do not carry a channel name.
220+ Some ( Channel :: Stable ) | None => 0 ,
221+ }
222+ }
223+
224+ toolchains. sort_unstable_by ( |a, b| {
225+ channel_preference ( & a. version )
226+ . cmp ( & channel_preference ( & b. version ) )
227+ . then_with ( || b. version . cmp ( & a. version ) )
228+ . then_with ( || a. name . cmp ( & b. name ) )
229+ . then_with ( || a. path . cmp ( & b. path ) )
230+ } ) ;
231+ }
232+
233+ #[ cfg( test) ]
234+ mod tests {
235+ use super :: * ;
236+
237+ fn toolchain ( name : & str , version : & semver:: Version , toolchain_name : & str ) -> ToolchainInfo {
238+ let path = PathBuf :: from ( "/rustup/toolchains" ) . join ( toolchain_name) ;
239+ ToolchainInfo {
240+ name : name. to_string ( ) ,
241+ version : RustVersion :: from_path ( version, & path) ,
242+ path,
243+ }
244+ }
245+
246+ #[ test]
247+ fn preferred_toolchain_order_prefers_release_over_newer_nightly ( ) {
248+ let mut toolchains = vec ! [
249+ toolchain(
250+ "nightly-aarch64-apple-darwin" ,
251+ & semver:: Version :: new( 1 , 97 , 0 ) ,
252+ "nightly-aarch64-apple-darwin" ,
253+ ) ,
254+ toolchain(
255+ "stable-aarch64-apple-darwin" ,
256+ & semver:: Version :: new( 1 , 90 , 0 ) ,
257+ "stable-aarch64-apple-darwin" ,
258+ ) ,
259+ ] ;
260+
261+ sort_toolchains ( & mut toolchains) ;
262+ let selected = toolchains
263+ . into_iter ( )
264+ . find ( |info| RustRequest :: Any . matches ( & info. version , Some ( & info. path ) ) )
265+ . expect ( "matching toolchain" ) ;
266+
267+ assert_eq ! ( selected. name, "stable-aarch64-apple-darwin" ) ;
268+ }
269+
270+ #[ test]
271+ fn preferred_toolchain_order_uses_nightly_when_it_is_the_only_candidate ( ) {
272+ let mut toolchains = vec ! [ toolchain(
273+ "nightly-aarch64-apple-darwin" ,
274+ & semver:: Version :: new( 1 , 97 , 0 ) ,
275+ "nightly-aarch64-apple-darwin" ,
276+ ) ] ;
277+
278+ sort_toolchains ( & mut toolchains) ;
279+ let selected = toolchains
280+ . into_iter ( )
281+ . find ( |info| RustRequest :: Any . matches ( & info. version , Some ( & info. path ) ) )
282+ . expect ( "matching toolchain" ) ;
283+
284+ assert_eq ! ( selected. name, "nightly-aarch64-apple-darwin" ) ;
285+ }
286+ }
0 commit comments