term_color_support/colors/
mod.rs1use atty::{ Stream, is };
29
30use crate::environment::Environment;
31use crate::options::{
32 OutputStreamOptions,
33 has_flag,
34 extract_force_color_level_from_env,
35 extract_color_level_from_flags,
36};
37
38#[derive(Debug, PartialEq)]
40pub enum ColorSupportLevel {
41 NoColor,
43 Basic,
45 Colors256,
47 TrueColor,
49}
50
51impl ColorSupportLevel {
52 pub fn from_u32(level: u32) -> Option<ColorSupportLevel> {
54 match level {
55 0 => Some(ColorSupportLevel::NoColor),
56 1 => Some(ColorSupportLevel::Basic),
57 2 => Some(ColorSupportLevel::Colors256),
58 3 => Some(ColorSupportLevel::TrueColor),
59 _ => None,
60 }
61 }
62}
63
64#[derive(Debug, PartialEq)]
66pub struct ColorInfo {
67 pub level: ColorSupportLevel,
69 pub has_basic: bool,
71 pub has_256: bool,
73 pub has_16m: bool,
75}
76
77impl ColorInfo {
78 pub fn new(level: ColorSupportLevel) -> Self {
80 let (has_basic, has_256, has_16m) = match level {
81 ColorSupportLevel::NoColor => (false, false, false),
82 ColorSupportLevel::Basic => (true, false, false),
83 ColorSupportLevel::Colors256 => (true, true, false),
84 ColorSupportLevel::TrueColor => (true, true, true),
85 };
86
87 ColorInfo {
88 level,
89 has_basic,
90 has_256,
91 has_16m,
92 }
93 }
94}
95
96#[derive(Debug)]
98pub struct ColorSupport {
99 pub stdout: ColorInfo,
101 pub stderr: ColorInfo,
103}
104
105impl ColorSupport {
106 pub fn stdout() -> ColorInfo {
108 let stdout_color_support_level: Option<ColorSupportLevel> = determine_stream_color_level(
109 OutputStreamOptions::new(Some(is(Stream::Stdout)), None)
110 );
111 ColorInfo::new(stdout_color_support_level.unwrap_or(ColorSupportLevel::NoColor))
112 }
113
114 pub fn stderr() -> ColorInfo {
116 let stderr_color_support_level: Option<ColorSupportLevel> = determine_stream_color_level(
117 OutputStreamOptions::new(Some(is(Stream::Stderr)), None)
118 );
119 ColorInfo::new(stderr_color_support_level.unwrap_or(ColorSupportLevel::NoColor))
120 }
121}
122
123pub fn determine_stream_color_level(options: OutputStreamOptions) -> Option<ColorSupportLevel> {
125 let args = std::env::args().collect::<Vec<String>>();
126
127 let force_color_level_from_env = extract_force_color_level_from_env();
128
129 let mut color_level_from_flag: Option<ColorSupportLevel> = Some(ColorSupportLevel::NoColor);
130
131 if force_color_level_from_env.is_none() {
132 color_level_from_flag = extract_color_level_from_flags(&args);
133 }
134
135 let force_color = if options.sniff_flags == true {
136 color_level_from_flag
137 } else {
138 force_color_level_from_env
139 };
140
141 if force_color.is_some() {
142 return force_color;
143 }
144
145 if options.sniff_flags {
146 if
147 has_flag("color=16m", &args) ||
148 has_flag("color=full", &args) ||
149 has_flag("color=truecolor", &args)
150 {
151 return Some(ColorSupportLevel::TrueColor);
152 }
153 if has_flag("color=256", &args) {
154 return Some(ColorSupportLevel::Colors256);
155 }
156 }
157
158 if !options.is_tty && force_color.is_none() {
159 return Some(ColorSupportLevel::NoColor);
160 }
161
162 let environment = Environment::default();
163 Some(environment.determine_color_level())
164}
165
166#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_color_support_level_from_u32() {
174 assert_eq!(ColorSupportLevel::from_u32(0), Some(ColorSupportLevel::NoColor));
175 assert_eq!(ColorSupportLevel::from_u32(1), Some(ColorSupportLevel::Basic));
176 assert_eq!(ColorSupportLevel::from_u32(2), Some(ColorSupportLevel::Colors256));
177 assert_eq!(ColorSupportLevel::from_u32(3), Some(ColorSupportLevel::TrueColor));
178 assert_eq!(ColorSupportLevel::from_u32(4), None);
179 }
180
181 #[test]
182 fn test_color_info_new() {
183 let color_info = ColorInfo::new(ColorSupportLevel::Basic);
184 assert_eq!(color_info.level, ColorSupportLevel::Basic);
185 assert_eq!(color_info.has_basic, true);
186 assert_eq!(color_info.has_256, false);
187 assert_eq!(color_info.has_16m, false);
188 }
189
190 #[test]
191 fn test_color_support_stderr() {
192 let _ = ColorSupport::stderr();
194 }
195
196 #[test]
197 fn test_determine_stream_color_level() {
198 let _ = determine_stream_color_level(OutputStreamOptions::new(Some(false), None));
200 }
201
202 #[test]
204 fn test_color_support_stdout() {
205 let _ = ColorSupport::stdout();
207 }
208
209 #[test]
211 fn test_color_support_level_equality() {
212 assert_eq!(ColorSupportLevel::NoColor, ColorSupportLevel::NoColor);
213 assert_eq!(ColorSupportLevel::Basic, ColorSupportLevel::Basic);
214 assert_eq!(ColorSupportLevel::Colors256, ColorSupportLevel::Colors256);
215 assert_eq!(ColorSupportLevel::TrueColor, ColorSupportLevel::TrueColor);
216 }
217
218 #[test]
220 fn test_color_info_equality() {
221 let color_info1 = ColorInfo::new(ColorSupportLevel::Basic);
222 let color_info2 = ColorInfo::new(ColorSupportLevel::Basic);
223 assert_eq!(color_info1, color_info2);
224 }
225
226 #[test]
228 fn test_color_info_inequality() {
229 let color_info1 = ColorInfo::new(ColorSupportLevel::Basic);
230 let color_info2 = ColorInfo::new(ColorSupportLevel::TrueColor);
231 assert_ne!(color_info1, color_info2);
232 }
233}