@@ -11,14 +11,16 @@ use termcolor::{ColorChoice, ColorSpec, StandardStream, StandardStreamLock, Writ
11
11
12
12
#[ cfg( feature = "test" ) ]
13
13
use super :: filesource:: { TestWriter , TestWriterLock } ;
14
- use super :: process;
14
+ use super :: { process, varsource :: VarSource } ;
15
15
16
16
/// Select what stream to make a terminal on
17
17
pub ( super ) enum StreamSelector {
18
18
Stdout ,
19
19
Stderr ,
20
20
#[ cfg( feature = "test" ) ]
21
21
TestWriter ( TestWriter ) ,
22
+ #[ cfg( feature = "test" ) ]
23
+ TestTtyWriter ( TestWriter ) ,
22
24
}
23
25
24
26
impl StreamSelector {
@@ -36,6 +38,8 @@ impl StreamSelector {
36
38
} ,
37
39
#[ cfg( feature = "test" ) ]
38
40
StreamSelector :: TestWriter ( _) => false ,
41
+ #[ cfg( feature = "test" ) ]
42
+ StreamSelector :: TestTtyWriter ( _) => true ,
39
43
}
40
44
}
41
45
}
@@ -55,7 +59,7 @@ pub struct ColorableTerminal {
55
59
enum TerminalInner {
56
60
StandardStream ( StandardStream , ColorSpec ) ,
57
61
#[ cfg( feature = "test" ) ]
58
- TestWriter ( TestWriter ) ,
62
+ TestWriter ( TestWriter , ColorChoice ) ,
59
63
}
60
64
61
65
pub struct ColorableTerminalLocked {
@@ -73,14 +77,20 @@ enum TerminalInnerLocked {
73
77
}
74
78
75
79
impl ColorableTerminal {
76
- /// Construct a terminal for the selected stream. Colors written to the
77
- /// terminal will be discarded if the stream is not a tty.
80
+ /// A terminal that supports colorisation of a stream.
81
+ /// If `RUSTUP_TERM_COLOR` is set to `always`, or if the stream is a tty and
82
+ /// `RUSTUP_TERM_COLOR` either unset or set to `auto`,
83
+ /// then color commands will be sent to the stream.
84
+ /// Otherwise color commands are discarded.
78
85
pub ( super ) fn new ( stream : StreamSelector ) -> Self {
79
- let is_a_tty = stream. is_a_tty ( ) ;
80
- let choice = if is_a_tty {
81
- ColorChoice :: Auto
82
- } else {
83
- ColorChoice :: Never
86
+ let env_override = process ( )
87
+ . var ( "RUSTUP_TERM_COLOR" )
88
+ . map ( |it| it. to_lowercase ( ) ) ;
89
+ let choice = match env_override. as_deref ( ) {
90
+ Ok ( "always" ) => ColorChoice :: Always ,
91
+ Ok ( "never" ) => ColorChoice :: Never ,
92
+ _ if stream. is_a_tty ( ) => ColorChoice :: Auto ,
93
+ _ => ColorChoice :: Never ,
84
94
} ;
85
95
let inner = match stream {
86
96
StreamSelector :: Stdout => {
@@ -90,7 +100,9 @@ impl ColorableTerminal {
90
100
TerminalInner :: StandardStream ( StandardStream :: stderr ( choice) , ColorSpec :: new ( ) )
91
101
}
92
102
#[ cfg( feature = "test" ) ]
93
- StreamSelector :: TestWriter ( w) => TerminalInner :: TestWriter ( w) ,
103
+ StreamSelector :: TestWriter ( w) | StreamSelector :: TestTtyWriter ( w) => {
104
+ TerminalInner :: TestWriter ( w, choice)
105
+ }
94
106
} ;
95
107
ColorableTerminal {
96
108
inner : Arc :: new ( Mutex :: new ( inner) ) ,
@@ -118,7 +130,7 @@ impl ColorableTerminal {
118
130
TerminalInnerLocked :: StandardStream ( locked)
119
131
}
120
132
#[ cfg( feature = "test" ) ]
121
- TerminalInner :: TestWriter ( w) => TerminalInnerLocked :: TestWriter ( w. lock ( ) ) ,
133
+ TerminalInner :: TestWriter ( w, _ ) => TerminalInnerLocked :: TestWriter ( w. lock ( ) ) ,
122
134
} ) ;
123
135
// ColorableTerminalLocked { inner, guard, locked }
124
136
uninit. assume_init ( )
@@ -132,7 +144,7 @@ impl ColorableTerminal {
132
144
s. set_color ( spec)
133
145
}
134
146
#[ cfg( feature = "test" ) ]
135
- TerminalInner :: TestWriter ( _) => Ok ( ( ) ) ,
147
+ TerminalInner :: TestWriter ( _, _ ) => Ok ( ( ) ) ,
136
148
}
137
149
}
138
150
@@ -143,7 +155,7 @@ impl ColorableTerminal {
143
155
s. set_color ( spec)
144
156
}
145
157
#[ cfg( feature = "test" ) ]
146
- TerminalInner :: TestWriter ( _) => Ok ( ( ) ) ,
158
+ TerminalInner :: TestWriter ( _, _ ) => Ok ( ( ) ) ,
147
159
}
148
160
}
149
161
@@ -157,23 +169,23 @@ impl ColorableTerminal {
157
169
s. set_color ( spec)
158
170
}
159
171
#[ cfg( feature = "test" ) ]
160
- TerminalInner :: TestWriter ( _) => Ok ( ( ) ) ,
172
+ TerminalInner :: TestWriter ( _, _ ) => Ok ( ( ) ) ,
161
173
}
162
174
}
163
175
164
176
pub fn reset ( & mut self ) -> io:: Result < ( ) > {
165
177
match self . inner . lock ( ) . unwrap ( ) . deref_mut ( ) {
166
178
TerminalInner :: StandardStream ( s, _color) => s. reset ( ) ,
167
179
#[ cfg( feature = "test" ) ]
168
- TerminalInner :: TestWriter ( _) => Ok ( ( ) ) ,
180
+ TerminalInner :: TestWriter ( _, _ ) => Ok ( ( ) ) ,
169
181
}
170
182
}
171
183
172
184
pub fn carriage_return ( & mut self ) -> io:: Result < ( ) > {
173
185
match self . inner . lock ( ) . unwrap ( ) . deref_mut ( ) {
174
186
TerminalInner :: StandardStream ( s, _color) => s. write ( b"\r " ) ?,
175
187
#[ cfg( feature = "test" ) ]
176
- TerminalInner :: TestWriter ( w) => w. write ( b"\r " ) ?,
188
+ TerminalInner :: TestWriter ( w, _ ) => w. write ( b"\r " ) ?,
177
189
} ;
178
190
Ok ( ( ) )
179
191
}
@@ -190,15 +202,15 @@ impl io::Write for ColorableTerminal {
190
202
match self . inner . lock ( ) . unwrap ( ) . deref_mut ( ) {
191
203
TerminalInner :: StandardStream ( s, _) => s. write ( buf) ,
192
204
#[ cfg( feature = "test" ) ]
193
- TerminalInner :: TestWriter ( w) => w. write ( buf) ,
205
+ TerminalInner :: TestWriter ( w, _ ) => w. write ( buf) ,
194
206
}
195
207
}
196
208
197
209
fn flush ( & mut self ) -> std:: result:: Result < ( ) , io:: Error > {
198
210
match self . inner . lock ( ) . unwrap ( ) . deref_mut ( ) {
199
211
TerminalInner :: StandardStream ( s, _) => s. flush ( ) ,
200
212
#[ cfg( feature = "test" ) ]
201
- TerminalInner :: TestWriter ( w) => w. flush ( ) ,
213
+ TerminalInner :: TestWriter ( w, _ ) => w. flush ( ) ,
202
214
}
203
215
}
204
216
}
@@ -220,3 +232,56 @@ impl io::Write for ColorableTerminalLocked {
220
232
}
221
233
}
222
234
}
235
+
236
+ #[ cfg( test) ]
237
+ mod tests {
238
+ use std:: collections:: HashMap ;
239
+
240
+ use rustup_macros:: unit_test as test;
241
+
242
+ use super :: * ;
243
+ use crate :: { currentprocess, test:: Env } ;
244
+
245
+ #[ test]
246
+ fn term_color_choice ( ) {
247
+ fn assert_color_choice ( env_val : & str , stream : StreamSelector , color_choice : ColorChoice ) {
248
+ let mut vars = HashMap :: new ( ) ;
249
+ vars. env ( "RUSTUP_TERM_COLOR" , env_val) ;
250
+ let tp = currentprocess:: TestProcess {
251
+ vars,
252
+ ..Default :: default ( )
253
+ } ;
254
+ currentprocess:: with ( tp. into ( ) , || {
255
+ let term = ColorableTerminal :: new ( stream) ;
256
+ let inner = term. inner . lock ( ) . unwrap ( ) ;
257
+ assert ! ( matches!(
258
+ & * inner,
259
+ & TerminalInner :: TestWriter ( _, choice) if choice == color_choice
260
+ ) ) ;
261
+ } ) ;
262
+ }
263
+
264
+ assert_color_choice (
265
+ "aLWayS" ,
266
+ StreamSelector :: TestWriter ( Default :: default ( ) ) ,
267
+ ColorChoice :: Always ,
268
+ ) ;
269
+ assert_color_choice (
270
+ "neVer" ,
271
+ StreamSelector :: TestWriter ( Default :: default ( ) ) ,
272
+ ColorChoice :: Never ,
273
+ ) ;
274
+ // tty + `auto` enables the colors.
275
+ assert_color_choice (
276
+ "AutO" ,
277
+ StreamSelector :: TestTtyWriter ( Default :: default ( ) ) ,
278
+ ColorChoice :: Auto ,
279
+ ) ;
280
+ // non-tty + `auto` does not enable the colors.
281
+ assert_color_choice (
282
+ "aUTo" ,
283
+ StreamSelector :: TestWriter ( Default :: default ( ) ) ,
284
+ ColorChoice :: Never ,
285
+ ) ;
286
+ }
287
+ }
0 commit comments