@@ -10,6 +10,22 @@ use std::path::Path;
10
10
11
11
pub ( crate ) const MAIN_TOOLCHAIN_NAME : & str = "stable" ;
12
12
13
+ /// Error caused by methods in the `toolchain` moodule.
14
+ #[ derive( Debug , failure:: Fail ) ]
15
+ #[ non_exhaustive]
16
+ pub enum ToolchainError {
17
+ /// The toolchain is not installed in the workspace, but the called method requires it to be
18
+ /// present. Use the [`Toolchain::Install`](struct.Toolchain.html#method.install) method to
19
+ /// install it inside the workspace.
20
+ #[ fail( display = "the toolchain is not installed" ) ]
21
+ NotInstalled ,
22
+ /// Not every method can be called with every kind of toolchain. If you receive this error
23
+ /// please check the documentation of the method you're calling to see which toolchains can you
24
+ /// use with it.
25
+ #[ fail( display = "unsupported operation on this toolchain" ) ]
26
+ UnsupportedOperation ,
27
+ }
28
+
13
29
/// Metadata of a dist toolchain. See [`Toolchain`](struct.Toolchain.html) to create and get it.
14
30
#[ derive( serde:: Serialize , serde:: Deserialize , PartialEq , Eq , Hash , Debug , Clone ) ]
15
31
pub struct DistToolchain {
@@ -39,6 +55,44 @@ impl DistToolchain {
39
55
}
40
56
}
41
57
58
+ #[ derive( Copy , Clone ) ]
59
+ enum RustupAction {
60
+ Add ,
61
+ Remove ,
62
+ }
63
+
64
+ impl std:: fmt:: Display for RustupAction {
65
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
66
+ write ! (
67
+ f,
68
+ "{}" ,
69
+ match self {
70
+ Self :: Add => "add" ,
71
+ Self :: Remove => "remove" ,
72
+ }
73
+ )
74
+ }
75
+ }
76
+
77
+ #[ derive( Copy , Clone ) ]
78
+ enum RustupThing {
79
+ Target ,
80
+ Component ,
81
+ }
82
+
83
+ impl std:: fmt:: Display for RustupThing {
84
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
85
+ write ! (
86
+ f,
87
+ "{}" ,
88
+ match self {
89
+ Self :: Target => "target" ,
90
+ Self :: Component => "component" ,
91
+ }
92
+ )
93
+ }
94
+ }
95
+
42
96
/// Metadata of a CI toolchain. See [`Toolchain`](struct.Toolchain.html) to create and get it.
43
97
#[ derive( serde:: Serialize , serde:: Deserialize , PartialEq , Eq , Hash , Debug , Clone ) ]
44
98
pub struct CiToolchain {
@@ -182,41 +236,118 @@ impl Toolchain {
182
236
183
237
/// Download and install a component for the toolchain.
184
238
pub fn add_component ( & self , workspace : & Workspace , name : & str ) -> Result < ( ) , Error > {
185
- self . add_rustup_thing ( workspace, "component" , name)
239
+ self . change_rustup_thing ( workspace, RustupAction :: Add , RustupThing :: Component , name)
186
240
}
187
241
188
242
/// Download and install a target for the toolchain.
243
+ ///
244
+ /// If the toolchain is not installed in the workspace an error will be returned. This is only
245
+ /// supported for dist toolchains.
189
246
pub fn add_target ( & self , workspace : & Workspace , name : & str ) -> Result < ( ) , Error > {
190
- self . add_rustup_thing ( workspace, "target" , name)
247
+ self . change_rustup_thing ( workspace, RustupAction :: Add , RustupThing :: Target , name)
191
248
}
192
249
193
- fn add_rustup_thing (
250
+ /// Remove a target already installed for the toolchain.
251
+ ///
252
+ /// If the toolchain is not installed in the workspace or the target is missing an error will
253
+ /// be returned. This is only supported for dist toolchains.
254
+ pub fn remove_target ( & self , workspace : & Workspace , name : & str ) -> Result < ( ) , Error > {
255
+ self . change_rustup_thing ( workspace, RustupAction :: Remove , RustupThing :: Target , name)
256
+ }
257
+
258
+ /// Return a list of installed targets for this toolchain.
259
+ ///
260
+ /// If the toolchain is not installed an empty list is returned.
261
+ pub fn installed_targets ( & self , workspace : & Workspace ) -> Result < Vec < String > , Error > {
262
+ self . list_rustup_things ( workspace, RustupThing :: Target )
263
+ }
264
+
265
+ fn change_rustup_thing (
194
266
& self ,
195
267
workspace : & Workspace ,
196
- thing : & str ,
268
+ action : RustupAction ,
269
+ thing : RustupThing ,
197
270
name : & str ,
198
271
) -> Result < ( ) , Error > {
272
+ let ( log_action, log_action_ing) = match action {
273
+ RustupAction :: Add => ( "add" , "adding" ) ,
274
+ RustupAction :: Remove => ( "remove" , "removing" ) ,
275
+ } ;
276
+
277
+ let thing = thing. to_string ( ) ;
278
+ let action = action. to_string ( ) ;
279
+
199
280
if let ToolchainInner :: CI { .. } = self . inner {
200
- bail ! ( "installing {} on CI toolchains is not supported yet" , thing) ;
281
+ bail ! (
282
+ "{} {} on CI toolchains is not supported yet" ,
283
+ log_action_ing,
284
+ thing
285
+ ) ;
201
286
}
202
287
let toolchain_name = self . rustup_name ( ) ;
203
288
info ! (
204
- "installing {} {} for toolchain {}" ,
205
- thing, name, toolchain_name
289
+ "{} {} {} for toolchain {}" ,
290
+ log_action_ing , thing, name, toolchain_name
206
291
) ;
207
292
208
293
Command :: new ( workspace, & RUSTUP )
209
- . args ( & [ thing, "add" , "--toolchain" , & toolchain_name, name] )
294
+ . args ( & [
295
+ thing. as_str ( ) ,
296
+ action. as_str ( ) ,
297
+ "--toolchain" ,
298
+ & toolchain_name,
299
+ name,
300
+ ] )
210
301
. run ( )
211
302
. with_context ( |_| {
212
303
format ! (
213
- "unable to install {} {} for toolchain {} via rustup" ,
214
- thing, name, toolchain_name,
304
+ "unable to {} {} {} for toolchain {} via rustup" ,
305
+ log_action , thing, name, toolchain_name,
215
306
)
216
307
} ) ?;
217
308
Ok ( ( ) )
218
309
}
219
310
311
+ fn list_rustup_things (
312
+ & self ,
313
+ workspace : & Workspace ,
314
+ thing : RustupThing ,
315
+ ) -> Result < Vec < String > , Error > {
316
+ let thing = thing. to_string ( ) ;
317
+ let name = if let Some ( dist) = self . as_dist ( ) {
318
+ dist. name ( )
319
+ } else {
320
+ return Err ( ToolchainError :: UnsupportedOperation . into ( ) ) ;
321
+ } ;
322
+
323
+ let mut not_installed = false ;
324
+ let result = Command :: new ( workspace, & RUSTUP )
325
+ . args ( & [ thing. as_str ( ) , "list" , "--installed" , "--toolchain" , name] )
326
+ . log_output ( false )
327
+ . process_lines ( & mut |line| {
328
+ if line. starts_with ( "error: toolchain " ) && line. ends_with ( " is not installed" ) {
329
+ not_installed = true ;
330
+ }
331
+ } )
332
+ . run_capture ( ) ;
333
+
334
+ match result {
335
+ Ok ( out) => Ok ( out
336
+ . stdout_lines ( )
337
+ . iter ( )
338
+ . filter ( |line| !line. is_empty ( ) )
339
+ . cloned ( )
340
+ . collect ( ) ) ,
341
+ Err ( _) if not_installed => Err ( ToolchainError :: NotInstalled . into ( ) ) ,
342
+ Err ( err) => Err ( err
343
+ . context ( format ! (
344
+ "failed to read the list of installed {}s for {} with rustup" ,
345
+ thing, name
346
+ ) )
347
+ . into ( ) ) ,
348
+ }
349
+ }
350
+
220
351
/// Remove the toolchain from the rustwide workspace, freeing up disk space.
221
352
pub fn uninstall ( & self , workspace : & Workspace ) -> Result < ( ) , Error > {
222
353
let name = self . rustup_name ( ) ;
@@ -305,7 +436,7 @@ impl Runnable for RustupProxy<'_> {
305
436
}
306
437
}
307
438
308
- pub ( crate ) fn list_installed ( rustup_home : & Path ) -> Result < Vec < Toolchain > , Error > {
439
+ pub ( crate ) fn list_installed_toolchains ( rustup_home : & Path ) -> Result < Vec < Toolchain > , Error > {
309
440
let update_hashes = rustup_home. join ( "update-hashes" ) ;
310
441
311
442
let mut result = Vec :: new ( ) ;
@@ -395,7 +526,7 @@ mod tests {
395
526
. join ( format ! ( "{}-alt" , CI_SHA ) ) ,
396
527
) ?;
397
528
398
- let res = super :: list_installed ( rustup_home. path ( ) ) ?;
529
+ let res = super :: list_installed_toolchains ( rustup_home. path ( ) ) ?;
399
530
assert_eq ! ( 4 , res. len( ) ) ;
400
531
assert ! ( res. contains( & Toolchain :: dist( DIST_NAME ) ) ) ;
401
532
assert ! ( res. contains( & Toolchain :: dist( LINK_NAME ) ) ) ;
0 commit comments