1use anyhow::Result;
11use std::sync::OnceLock;
12use termcolor::ColorChoice;
13
14static COR_CACHE: OnceLock<ColorChoice> = OnceLock::new();
16
17pub fn inicializar(sem_cor: bool) -> Result<()> {
22 let escolha = determinar_cor(sem_cor);
23 let _ = COR_CACHE.set(escolha);
24 tracing::debug!("configuração de cor do terminal: {:?}", escolha);
25 Ok(())
26}
27
28#[must_use]
33pub fn cor_escolha() -> ColorChoice {
34 *COR_CACHE.get().unwrap_or(&ColorChoice::Never)
35}
36
37#[must_use]
42pub fn e_interativo() -> bool {
43 use std::io::IsTerminal;
44
45 if std::env::var("TERM").as_deref() == Ok("dumb") {
47 return false;
48 }
49
50 std::io::stdout().is_terminal()
51}
52
53fn determinar_cor(sem_cor_cli: bool) -> ColorChoice {
55 if sem_cor_cli {
57 return ColorChoice::Never;
58 }
59
60 if std::env::var("NO_COLOR").is_ok() {
62 return ColorChoice::Never;
63 }
64
65 if std::env::var("CLICOLOR_FORCE").as_deref() == Ok("1") {
67 return ColorChoice::Always;
68 }
69
70 if e_interativo() {
72 ColorChoice::Auto
73 } else {
74 ColorChoice::Never
75 }
76}
77
78#[cfg(test)]
79mod testes {
80 use super::*;
81 use serial_test::serial;
82
83 #[test]
84 fn sem_cor_cli_retorna_never() {
85 let escolha = determinar_cor(true);
86 assert!(matches!(escolha, ColorChoice::Never));
87 }
88
89 #[test]
90 #[serial]
91 fn no_color_env_retorna_never() {
92 let anterior = std::env::var("NO_COLOR").ok();
94 let anterior_force = std::env::var("CLICOLOR_FORCE").ok();
95
96 std::env::set_var("NO_COLOR", "1");
97 std::env::remove_var("CLICOLOR_FORCE");
98
99 let escolha = determinar_cor(false);
100 assert!(matches!(escolha, ColorChoice::Never));
101
102 match anterior {
104 Some(v) => std::env::set_var("NO_COLOR", v),
105 None => std::env::remove_var("NO_COLOR"),
106 }
107 match anterior_force {
108 Some(v) => std::env::set_var("CLICOLOR_FORCE", v),
109 None => std::env::remove_var("CLICOLOR_FORCE"),
110 }
111 }
112
113 #[test]
114 #[serial]
115 fn clicolor_force_retorna_always() {
116 let anterior = std::env::var("NO_COLOR").ok();
117 let anterior_force = std::env::var("CLICOLOR_FORCE").ok();
118
119 std::env::remove_var("NO_COLOR");
120 std::env::set_var("CLICOLOR_FORCE", "1");
121
122 let escolha = determinar_cor(false);
123 assert!(matches!(escolha, ColorChoice::Always));
124
125 match anterior {
127 Some(v) => std::env::set_var("NO_COLOR", v),
128 None => std::env::remove_var("NO_COLOR"),
129 }
130 match anterior_force {
131 Some(v) => std::env::set_var("CLICOLOR_FORCE", v),
132 None => std::env::remove_var("CLICOLOR_FORCE"),
133 }
134 }
135
136 #[test]
137 fn cor_escolha_retorna_never_sem_inicializar() {
138 let _ = cor_escolha();
142 }
143
144 #[test]
145 fn e_interativo_retorna_bool() {
146 let _ = e_interativo();
148 }
149
150 #[test]
151 fn inicializar_com_sem_cor_true_nao_panica() {
152 let resultado = inicializar(true);
156 assert!(resultado.is_ok());
157 }
158
159 #[test]
160 fn inicializar_com_sem_cor_false_nao_panica() {
161 let resultado = inicializar(false);
164 assert!(resultado.is_ok());
165 }
166
167 #[test]
168 #[serial]
169 fn e_interativo_com_term_dumb_retorna_false() {
170 let anterior = std::env::var("TERM").ok();
172
173 std::env::set_var("TERM", "dumb");
174 let resultado = e_interativo();
175 assert!(!resultado, "TERM=dumb deve forçar modo não-interativo");
176
177 match anterior {
179 Some(v) => std::env::set_var("TERM", v),
180 None => std::env::remove_var("TERM"),
181 }
182 }
183
184 #[test]
185 #[serial]
186 fn determinar_cor_sem_env_vars_retorna_never_em_ambiente_nao_tty() {
187 let anterior_no = std::env::var("NO_COLOR").ok();
190 let anterior_force = std::env::var("CLICOLOR_FORCE").ok();
191 let anterior_term = std::env::var("TERM").ok();
192
193 std::env::remove_var("NO_COLOR");
194 std::env::remove_var("CLICOLOR_FORCE");
195 std::env::set_var("TERM", "dumb");
196
197 let escolha = determinar_cor(false);
198 assert!(matches!(escolha, ColorChoice::Never));
201
202 match anterior_no {
204 Some(v) => std::env::set_var("NO_COLOR", v),
205 None => std::env::remove_var("NO_COLOR"),
206 }
207 match anterior_force {
208 Some(v) => std::env::set_var("CLICOLOR_FORCE", v),
209 None => std::env::remove_var("CLICOLOR_FORCE"),
210 }
211 match anterior_term {
212 Some(v) => std::env::set_var("TERM", v),
213 None => std::env::remove_var("TERM"),
214 }
215 }
216}