@@ -35,6 +35,134 @@ use crate::{
35
35
DropErrorDetailsExt ,
36
36
} ;
37
37
38
+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
39
+ pub struct Versions {
40
+ pub stable : ChannelVersions ,
41
+ pub beta : ChannelVersions ,
42
+ pub nightly : ChannelVersions ,
43
+ }
44
+
45
+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
46
+ pub struct ChannelVersions {
47
+ pub rustc : Version ,
48
+ pub rustfmt : Version ,
49
+ pub clippy : Version ,
50
+ pub miri : Option < Version > ,
51
+ }
52
+
53
+ /// Parsing this struct is very lenient — we'd rather return some
54
+ /// partial data instead of absolutely nothing.
55
+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
56
+ pub struct Version {
57
+ pub release : String ,
58
+ pub commit_hash : String ,
59
+ pub commit_date : String ,
60
+ }
61
+
62
+ impl Version {
63
+ fn parse_rustc_version_verbose ( rustc_version : & str ) -> Self {
64
+ let mut release = "" ;
65
+ let mut commit_hash = "" ;
66
+ let mut commit_date = "" ;
67
+
68
+ let fields = rustc_version. lines ( ) . skip ( 1 ) . filter_map ( |line| {
69
+ let mut pieces = line. splitn ( 2 , ':' ) ;
70
+ let key = pieces. next ( ) ?. trim ( ) ;
71
+ let value = pieces. next ( ) ?. trim ( ) ;
72
+ Some ( ( key, value) )
73
+ } ) ;
74
+
75
+ for ( k, v) in fields {
76
+ match k {
77
+ "release" => release = v,
78
+ "commit-hash" => commit_hash = v,
79
+ "commit-date" => commit_date = v,
80
+ _ => { }
81
+ }
82
+ }
83
+
84
+ Self {
85
+ release : release. into ( ) ,
86
+ commit_hash : commit_hash. into ( ) ,
87
+ commit_date : commit_date. into ( ) ,
88
+ }
89
+ }
90
+
91
+ // Parses versions of the shape `toolname 0.0.0 (0000000 0000-00-00)`
92
+ fn parse_tool_version ( tool_version : & str ) -> Self {
93
+ let mut parts = tool_version. split_whitespace ( ) . fuse ( ) . skip ( 1 ) ;
94
+
95
+ let release = parts. next ( ) . unwrap_or ( "" ) . into ( ) ;
96
+ let commit_hash = parts. next ( ) . unwrap_or ( "" ) . trim_start_matches ( '(' ) . into ( ) ;
97
+ let commit_date = parts. next ( ) . unwrap_or ( "" ) . trim_end_matches ( ')' ) . into ( ) ;
98
+
99
+ Self {
100
+ release,
101
+ commit_hash,
102
+ commit_date,
103
+ }
104
+ }
105
+ }
106
+
107
+ #[ derive( Debug , Snafu ) ]
108
+ #[ snafu( module) ]
109
+ pub enum VersionsError {
110
+ #[ snafu( display( "Unable to determine versions for the stable channel" ) ) ]
111
+ Stable { source : VersionsChannelError } ,
112
+
113
+ #[ snafu( display( "Unable to determine versions for the beta channel" ) ) ]
114
+ Beta { source : VersionsChannelError } ,
115
+
116
+ #[ snafu( display( "Unable to determine versions for the nightly channel" ) ) ]
117
+ Nightly { source : VersionsChannelError } ,
118
+ }
119
+
120
+ #[ derive( Debug , Snafu ) ]
121
+ pub enum VersionsChannelError {
122
+ #[ snafu( context( false ) ) ] // transparent
123
+ Channel { source : Error } ,
124
+
125
+ #[ snafu( context( false ) ) ] // transparent
126
+ Versions { source : ContainerVersionsError } ,
127
+ }
128
+
129
+ #[ derive( Debug , Snafu ) ]
130
+ #[ snafu( module) ]
131
+ pub enum ContainerVersionsError {
132
+ #[ snafu( display( "Failed to get `rustc` version" ) ) ]
133
+ Rustc { source : VersionError } ,
134
+
135
+ #[ snafu( display( "`rustc` not executable" ) ) ]
136
+ RustcMissing ,
137
+
138
+ #[ snafu( display( "Failed to get `rustfmt` version" ) ) ]
139
+ Rustfmt { source : VersionError } ,
140
+
141
+ #[ snafu( display( "`cargo fmt` not executable" ) ) ]
142
+ RustfmtMissing ,
143
+
144
+ #[ snafu( display( "Failed to get clippy version" ) ) ]
145
+ Clippy { source : VersionError } ,
146
+
147
+ #[ snafu( display( "`cargo clippy` not executable" ) ) ]
148
+ ClippyMissing ,
149
+
150
+ #[ snafu( display( "Failed to get miri version" ) ) ]
151
+ Miri { source : VersionError } ,
152
+ }
153
+
154
+ #[ derive( Debug , Snafu ) ]
155
+ #[ snafu( module) ]
156
+ pub enum VersionError {
157
+ #[ snafu( display( "Could not start the process" ) ) ]
158
+ #[ snafu( context( false ) ) ]
159
+ SpawnProcess { source : SpawnCargoError } ,
160
+
161
+ #[ snafu( display( "The task panicked" ) ) ]
162
+ #[ snafu( context( false ) ) ]
163
+ TaskPanic { source : tokio:: task:: JoinError } ,
164
+ }
165
+
38
166
#[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
39
167
pub enum AssemblyFlavor {
40
168
Att ,
@@ -639,6 +767,28 @@ where
639
767
}
640
768
}
641
769
770
+ pub async fn versions ( & self ) -> Result < Versions , VersionsError > {
771
+ use versions_error:: * ;
772
+
773
+ let [ stable, beta, nightly] =
774
+ [ Channel :: Stable , Channel :: Beta , Channel :: Nightly ] . map ( |c| async move {
775
+ let c = self . select_channel ( c) . await ?;
776
+ c. versions ( ) . await . map_err ( VersionsChannelError :: from)
777
+ } ) ;
778
+
779
+ let ( stable, beta, nightly) = join ! ( stable, beta, nightly) ;
780
+
781
+ let stable = stable. context ( StableSnafu ) ?;
782
+ let beta = beta. context ( BetaSnafu ) ?;
783
+ let nightly = nightly. context ( NightlySnafu ) ?;
784
+
785
+ Ok ( Versions {
786
+ stable,
787
+ beta,
788
+ nightly,
789
+ } )
790
+ }
791
+
642
792
pub async fn execute (
643
793
& self ,
644
794
request : ExecuteRequest ,
@@ -904,6 +1054,72 @@ impl Container {
904
1054
} )
905
1055
}
906
1056
1057
+ async fn versions ( & self ) -> Result < ChannelVersions , ContainerVersionsError > {
1058
+ use container_versions_error:: * ;
1059
+
1060
+ let token = CancellationToken :: new ( ) ;
1061
+
1062
+ let rustc = self . rustc_version ( token. clone ( ) ) ;
1063
+ let rustfmt = self . tool_version ( token. clone ( ) , "fmt" ) ;
1064
+ let clippy = self . tool_version ( token. clone ( ) , "clippy" ) ;
1065
+ let miri = self . tool_version ( token, "miri" ) ;
1066
+
1067
+ let ( rustc, rustfmt, clippy, miri) = join ! ( rustc, rustfmt, clippy, miri) ;
1068
+
1069
+ let rustc = rustc. context ( RustcSnafu ) ?. context ( RustcMissingSnafu ) ?;
1070
+ let rustfmt = rustfmt
1071
+ . context ( RustfmtSnafu ) ?
1072
+ . context ( RustfmtMissingSnafu ) ?;
1073
+ let clippy = clippy. context ( ClippySnafu ) ?. context ( ClippyMissingSnafu ) ?;
1074
+ let miri = miri. context ( MiriSnafu ) ?;
1075
+
1076
+ Ok ( ChannelVersions {
1077
+ rustc,
1078
+ rustfmt,
1079
+ clippy,
1080
+ miri,
1081
+ } )
1082
+ }
1083
+
1084
+ async fn rustc_version (
1085
+ & self ,
1086
+ token : CancellationToken ,
1087
+ ) -> Result < Option < Version > , VersionError > {
1088
+ let rustc_cmd = ExecuteCommandRequest :: simple ( "rustc" , [ "--version" , "--verbose" ] ) ;
1089
+ let output = self . version_output ( token, rustc_cmd) . await ?;
1090
+
1091
+ Ok ( output. map ( |o| Version :: parse_rustc_version_verbose ( & o) ) )
1092
+ }
1093
+
1094
+ async fn tool_version (
1095
+ & self ,
1096
+ token : CancellationToken ,
1097
+ subcommand_name : & str ,
1098
+ ) -> Result < Option < Version > , VersionError > {
1099
+ let tool_cmd = ExecuteCommandRequest :: simple ( "cargo" , [ subcommand_name, "--version" ] ) ;
1100
+ let output = self . version_output ( token, tool_cmd) . await ?;
1101
+
1102
+ Ok ( output. map ( |o| Version :: parse_tool_version ( & o) ) )
1103
+ }
1104
+
1105
+ async fn version_output (
1106
+ & self ,
1107
+ token : CancellationToken ,
1108
+ cmd : ExecuteCommandRequest ,
1109
+ ) -> Result < Option < String > , VersionError > {
1110
+ let v = self . spawn_cargo_task ( token. clone ( ) , cmd) . await ?;
1111
+ let SpawnCargo {
1112
+ task,
1113
+ stdin_tx,
1114
+ stdout_rx,
1115
+ stderr_rx,
1116
+ } = v;
1117
+ drop ( stdin_tx) ;
1118
+ let task = async { task. await ?. map_err ( VersionError :: from) } ;
1119
+ let o = WithOutput :: try_absorb ( task, stdout_rx, stderr_rx) . await ?;
1120
+ Ok ( if o. success { Some ( o. stdout ) } else { None } )
1121
+ }
1122
+
907
1123
async fn execute (
908
1124
& self ,
909
1125
request : ExecuteRequest ,
@@ -2416,6 +2632,20 @@ mod tests {
2416
2632
}
2417
2633
}
2418
2634
2635
+ #[ tokio:: test]
2636
+ #[ snafu:: report]
2637
+ async fn versions ( ) -> Result < ( ) > {
2638
+ let coordinator = new_coordinator ( ) . await ;
2639
+
2640
+ let versions = coordinator. versions ( ) . with_timeout ( ) . await . unwrap ( ) ;
2641
+
2642
+ assert_starts_with ! ( versions. stable. rustc. release, "1." ) ;
2643
+
2644
+ coordinator. shutdown ( ) . await ?;
2645
+
2646
+ Ok ( ( ) )
2647
+ }
2648
+
2419
2649
const ARBITRARY_EXECUTE_REQUEST : ExecuteRequest = ExecuteRequest {
2420
2650
channel : Channel :: Stable ,
2421
2651
mode : Mode :: Debug ,
0 commit comments