nu_protocol/config/
ansi_coloring.rs1use super::{ConfigErrors, ConfigPath, IntoValue, ShellError, UpdateFromValue, Value};
2use crate::{self as nu_protocol, engine::EngineState, FromValue};
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!(engine_state
140 .get_config()
141 .use_ansi_coloring
142 .get(&engine_state));
143
144 set_env(&mut engine_state, "clicolor", false);
145 assert!(engine_state
146 .get_config()
147 .use_ansi_coloring
148 .get(&engine_state));
149 set_env(&mut engine_state, "clicolor", true);
150 assert!(engine_state
151 .get_config()
152 .use_ansi_coloring
153 .get(&engine_state));
154 set_env(&mut engine_state, "no_color", true);
155 assert!(engine_state
156 .get_config()
157 .use_ansi_coloring
158 .get(&engine_state));
159 set_env(&mut engine_state, "force_color", true);
160 assert!(engine_state
161 .get_config()
162 .use_ansi_coloring
163 .get(&engine_state));
164 }
165
166 #[test]
167 fn test_use_ansi_coloring_false() {
168 let mut engine_state = EngineState::new();
169 engine_state.config = Config {
170 use_ansi_coloring: UseAnsiColoring::False,
171 ..Default::default()
172 }
173 .into();
174
175 assert!(!engine_state
177 .get_config()
178 .use_ansi_coloring
179 .get(&engine_state));
180
181 set_env(&mut engine_state, "clicolor", false);
182 assert!(!engine_state
183 .get_config()
184 .use_ansi_coloring
185 .get(&engine_state));
186 set_env(&mut engine_state, "clicolor", true);
187 assert!(!engine_state
188 .get_config()
189 .use_ansi_coloring
190 .get(&engine_state));
191 set_env(&mut engine_state, "no_color", true);
192 assert!(!engine_state
193 .get_config()
194 .use_ansi_coloring
195 .get(&engine_state));
196 set_env(&mut engine_state, "force_color", true);
197 assert!(!engine_state
198 .get_config()
199 .use_ansi_coloring
200 .get(&engine_state));
201 }
202
203 #[test]
204 fn test_use_ansi_coloring_auto() {
205 let mut engine_state = EngineState::new();
206 engine_state.config = Config {
207 use_ansi_coloring: UseAnsiColoring::Auto,
208 ..Default::default()
209 }
210 .into();
211
212 let is_terminal = std::io::stdout().is_terminal();
214 assert_eq!(
215 engine_state
216 .get_config()
217 .use_ansi_coloring
218 .get(&engine_state),
219 is_terminal
220 );
221
222 set_env(&mut engine_state, "clicolor", true);
224 assert!(engine_state
225 .get_config()
226 .use_ansi_coloring
227 .get(&engine_state));
228
229 set_env(&mut engine_state, "clicolor", false);
230 assert!(!engine_state
231 .get_config()
232 .use_ansi_coloring
233 .get(&engine_state));
234
235 set_env(&mut engine_state, "no_color", true);
237 assert!(!engine_state
238 .get_config()
239 .use_ansi_coloring
240 .get(&engine_state));
241
242 set_env(&mut engine_state, "force_color", true);
244 assert!(engine_state
245 .get_config()
246 .use_ansi_coloring
247 .get(&engine_state));
248 }
249}