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_insensitive(env_name)
51 .map(|(_, v)| v)
52 .and_then(|v| v.coerce_bool().ok())
53 .unwrap_or(false)
54 };
55
56 if env_value("force_color") {
57 return true;
58 }
59
60 if env_value("no_color") {
61 return false;
62 }
63
64 if let Some((_, cli_color)) = engine_state.get_env_var_insensitive("clicolor") {
65 if let Ok(cli_color) = cli_color.coerce_bool() {
66 return cli_color;
67 }
68 }
69
70 is_terminal
71 }
72}
73
74impl From<bool> for UseAnsiColoring {
75 fn from(value: bool) -> Self {
76 match value {
77 true => Self::True,
78 false => Self::False,
79 }
80 }
81}
82
83impl FromValue for UseAnsiColoring {
84 fn from_value(v: Value) -> Result<Self, ShellError> {
85 if let Ok(v) = v.as_bool() {
86 return Ok(v.into());
87 }
88
89 #[derive(FromValue)]
90 enum UseAnsiColoringString {
91 Auto = 0,
92 True = 1,
93 False = 2,
94 }
95
96 Ok(match UseAnsiColoringString::from_value(v)? {
97 UseAnsiColoringString::Auto => Self::Auto,
98 UseAnsiColoringString::True => Self::True,
99 UseAnsiColoringString::False => Self::False,
100 })
101 }
102}
103
104impl UpdateFromValue for UseAnsiColoring {
105 fn update<'a>(
106 &mut self,
107 value: &'a Value,
108 path: &mut ConfigPath<'a>,
109 errors: &mut ConfigErrors,
110 ) {
111 let Ok(value) = UseAnsiColoring::from_value(value.clone()) else {
112 errors.type_mismatch(path, UseAnsiColoring::expected_type(), value);
113 return;
114 };
115
116 *self = value;
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use nu_protocol::Config;
124
125 fn set_env(engine_state: &mut EngineState, name: &str, value: bool) {
126 engine_state.add_env_var(name.to_string(), Value::test_bool(value));
127 }
128
129 #[test]
130 fn test_use_ansi_coloring_true() {
131 let mut engine_state = EngineState::new();
132 engine_state.config = Config {
133 use_ansi_coloring: UseAnsiColoring::True,
134 ..Default::default()
135 }
136 .into();
137
138 assert!(
140 engine_state
141 .get_config()
142 .use_ansi_coloring
143 .get(&engine_state)
144 );
145
146 set_env(&mut engine_state, "clicolor", false);
147 assert!(
148 engine_state
149 .get_config()
150 .use_ansi_coloring
151 .get(&engine_state)
152 );
153 set_env(&mut engine_state, "clicolor", true);
154 assert!(
155 engine_state
156 .get_config()
157 .use_ansi_coloring
158 .get(&engine_state)
159 );
160 set_env(&mut engine_state, "no_color", true);
161 assert!(
162 engine_state
163 .get_config()
164 .use_ansi_coloring
165 .get(&engine_state)
166 );
167 set_env(&mut engine_state, "force_color", true);
168 assert!(
169 engine_state
170 .get_config()
171 .use_ansi_coloring
172 .get(&engine_state)
173 );
174 }
175
176 #[test]
177 fn test_use_ansi_coloring_false() {
178 let mut engine_state = EngineState::new();
179 engine_state.config = Config {
180 use_ansi_coloring: UseAnsiColoring::False,
181 ..Default::default()
182 }
183 .into();
184
185 assert!(
187 !engine_state
188 .get_config()
189 .use_ansi_coloring
190 .get(&engine_state)
191 );
192
193 set_env(&mut engine_state, "clicolor", false);
194 assert!(
195 !engine_state
196 .get_config()
197 .use_ansi_coloring
198 .get(&engine_state)
199 );
200 set_env(&mut engine_state, "clicolor", true);
201 assert!(
202 !engine_state
203 .get_config()
204 .use_ansi_coloring
205 .get(&engine_state)
206 );
207 set_env(&mut engine_state, "no_color", true);
208 assert!(
209 !engine_state
210 .get_config()
211 .use_ansi_coloring
212 .get(&engine_state)
213 );
214 set_env(&mut engine_state, "force_color", true);
215 assert!(
216 !engine_state
217 .get_config()
218 .use_ansi_coloring
219 .get(&engine_state)
220 );
221 }
222
223 #[test]
224 fn test_use_ansi_coloring_auto() {
225 let mut engine_state = EngineState::new();
226 engine_state.config = Config {
227 use_ansi_coloring: UseAnsiColoring::Auto,
228 ..Default::default()
229 }
230 .into();
231
232 let is_terminal = std::io::stdout().is_terminal();
234 assert_eq!(
235 engine_state
236 .get_config()
237 .use_ansi_coloring
238 .get(&engine_state),
239 is_terminal
240 );
241
242 set_env(&mut engine_state, "clicolor", true);
244 assert!(
245 engine_state
246 .get_config()
247 .use_ansi_coloring
248 .get(&engine_state)
249 );
250
251 set_env(&mut engine_state, "clicolor", false);
252 assert!(
253 !engine_state
254 .get_config()
255 .use_ansi_coloring
256 .get(&engine_state)
257 );
258
259 set_env(&mut engine_state, "no_color", true);
261 assert!(
262 !engine_state
263 .get_config()
264 .use_ansi_coloring
265 .get(&engine_state)
266 );
267
268 set_env(&mut engine_state, "force_color", true);
270 assert!(
271 engine_state
272 .get_config()
273 .use_ansi_coloring
274 .get(&engine_state)
275 );
276 }
277}