nu_protocol/config/
ansi_coloring.rs1use super::{ConfigErrors, ConfigPath, IntoValue, ShellError, UpdateFromValue, Value};
2use crate::{self as nu_protocol, FromValue, engine::EngineState};
3use serde::{Deserialize, Serialize};
4use std::io::IsTerminal;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, IntoValue, Serialize, Deserialize)]
7pub enum UseAnsiColoring {
8 #[default]
9 Auto,
10 True,
11 False,
12}
13
14impl UseAnsiColoring {
15 pub fn get(self, engine_state: &EngineState) -> bool {
42 let is_terminal = match self {
43 Self::Auto => std::io::stdout().is_terminal(),
44 Self::True => return true,
45 Self::False => return false,
46 };
47
48 let env_value = |env_name| {
49 engine_state
50 .get_env_var(env_name)
51 .and_then(|v| v.coerce_bool().ok())
52 .unwrap_or(false)
53 };
54
55 if env_value("force_color") {
56 return true;
57 }
58
59 if env_value("no_color") {
60 return false;
61 }
62
63 if let Some(cli_color) = engine_state.get_env_var("clicolor")
64 && let Ok(cli_color) = cli_color.coerce_bool()
65 {
66 return cli_color;
67 }
68
69 if let Some(term) = engine_state.get_env_var("term")
71 && term.as_str().ok() == Some("dumb")
72 {
73 return false;
74 }
75
76 is_terminal
77 }
78}
79
80impl From<bool> for UseAnsiColoring {
81 fn from(value: bool) -> Self {
82 match value {
83 true => Self::True,
84 false => Self::False,
85 }
86 }
87}
88
89impl FromValue for UseAnsiColoring {
90 fn from_value(v: Value) -> Result<Self, ShellError> {
91 if let Ok(v) = v.as_bool() {
92 return Ok(v.into());
93 }
94
95 #[derive(FromValue)]
96 enum UseAnsiColoringString {
97 Auto = 0,
98 True = 1,
99 False = 2,
100 }
101
102 Ok(match UseAnsiColoringString::from_value(v)? {
103 UseAnsiColoringString::Auto => Self::Auto,
104 UseAnsiColoringString::True => Self::True,
105 UseAnsiColoringString::False => Self::False,
106 })
107 }
108}
109
110impl UpdateFromValue for UseAnsiColoring {
111 fn update<'a>(
112 &mut self,
113 value: &'a Value,
114 path: &mut ConfigPath<'a>,
115 errors: &mut ConfigErrors,
116 ) {
117 let Ok(value) = UseAnsiColoring::from_value(value.clone()) else {
118 errors.type_mismatch(path, UseAnsiColoring::expected_type(), value);
119 return;
120 };
121
122 *self = value;
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use nu_protocol::Config;
130
131 fn set_env(engine_state: &mut EngineState, name: &str, value: bool) {
132 engine_state.add_env_var(name.to_string(), Value::test_bool(value));
133 }
134
135 #[test]
136 fn test_use_ansi_coloring_true() {
137 let mut engine_state = EngineState::new();
138 engine_state.set_config(Config {
139 use_ansi_coloring: UseAnsiColoring::True,
140 ..Default::default()
141 });
142
143 assert!(
145 engine_state
146 .get_config()
147 .use_ansi_coloring
148 .get(&engine_state)
149 );
150
151 set_env(&mut engine_state, "clicolor", false);
152 assert!(
153 engine_state
154 .get_config()
155 .use_ansi_coloring
156 .get(&engine_state)
157 );
158 set_env(&mut engine_state, "clicolor", true);
159 assert!(
160 engine_state
161 .get_config()
162 .use_ansi_coloring
163 .get(&engine_state)
164 );
165 set_env(&mut engine_state, "no_color", true);
166 assert!(
167 engine_state
168 .get_config()
169 .use_ansi_coloring
170 .get(&engine_state)
171 );
172 set_env(&mut engine_state, "force_color", true);
173 assert!(
174 engine_state
175 .get_config()
176 .use_ansi_coloring
177 .get(&engine_state)
178 );
179 }
180
181 #[test]
182 fn test_use_ansi_coloring_false() {
183 let mut engine_state = EngineState::new();
184 engine_state.set_config(Config {
185 use_ansi_coloring: UseAnsiColoring::False,
186 ..Default::default()
187 });
188
189 assert!(
191 !engine_state
192 .get_config()
193 .use_ansi_coloring
194 .get(&engine_state)
195 );
196
197 set_env(&mut engine_state, "clicolor", false);
198 assert!(
199 !engine_state
200 .get_config()
201 .use_ansi_coloring
202 .get(&engine_state)
203 );
204 set_env(&mut engine_state, "clicolor", true);
205 assert!(
206 !engine_state
207 .get_config()
208 .use_ansi_coloring
209 .get(&engine_state)
210 );
211 set_env(&mut engine_state, "no_color", true);
212 assert!(
213 !engine_state
214 .get_config()
215 .use_ansi_coloring
216 .get(&engine_state)
217 );
218 set_env(&mut engine_state, "force_color", true);
219 assert!(
220 !engine_state
221 .get_config()
222 .use_ansi_coloring
223 .get(&engine_state)
224 );
225 }
226
227 #[test]
228 fn test_use_ansi_coloring_auto() {
229 let mut engine_state = EngineState::new();
230 engine_state.set_config(Config {
231 use_ansi_coloring: UseAnsiColoring::Auto,
232 ..Default::default()
233 });
234
235 let is_terminal = std::io::stdout().is_terminal();
237 assert_eq!(
238 engine_state
239 .get_config()
240 .use_ansi_coloring
241 .get(&engine_state),
242 is_terminal
243 );
244
245 set_env(&mut engine_state, "clicolor", true);
247 assert!(
248 engine_state
249 .get_config()
250 .use_ansi_coloring
251 .get(&engine_state)
252 );
253
254 set_env(&mut engine_state, "clicolor", false);
255 assert!(
256 !engine_state
257 .get_config()
258 .use_ansi_coloring
259 .get(&engine_state)
260 );
261
262 set_env(&mut engine_state, "no_color", true);
264 assert!(
265 !engine_state
266 .get_config()
267 .use_ansi_coloring
268 .get(&engine_state)
269 );
270
271 set_env(&mut engine_state, "force_color", true);
273 assert!(
274 engine_state
275 .get_config()
276 .use_ansi_coloring
277 .get(&engine_state)
278 );
279 }
280
281 #[test]
282 fn test_use_ansi_coloring_auto_term_dumb() {
283 let mut engine_state = EngineState::new();
284 engine_state.set_config(Config {
285 use_ansi_coloring: UseAnsiColoring::Auto,
286 ..Default::default()
287 });
288
289 engine_state.add_env_var("term".to_string(), Value::test_string("dumb"));
291 assert!(
292 !engine_state
293 .get_config()
294 .use_ansi_coloring
295 .get(&engine_state)
296 );
297 }
298}