1use core::fmt::{self, Display, Formatter};
2
3use crate::{
4 fmt::r#override::{AtomicOverride, AtomicPreference},
5 Report,
6};
7
8#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
13pub enum ColorMode {
14 None,
16
17 Color,
19
20 #[default]
27 Emphasis,
28}
29
30impl ColorMode {
31 pub(super) fn load() -> Self {
32 COLOR_OVERRIDE.load()
33 }
34}
35
36impl AtomicPreference for ColorMode {
43 fn from_u8(value: u8) -> Self {
44 match value {
45 0x00 => Self::None,
46 0x01 => Self::Color,
47 0x02 => Self::Emphasis,
48 _ => Self::default(),
49 }
50 }
51
52 fn into_u8(self) -> u8 {
53 match self {
54 Self::None => 0x00,
55 Self::Color => 0x01,
56 Self::Emphasis => 0x02,
57 }
58 }
59}
60
61static COLOR_OVERRIDE: AtomicOverride<ColorMode> = AtomicOverride::new();
62
63impl Report<()> {
64 #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/snapshots/doc/fmt__preference_none.snap"))]
123 #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/snapshots/doc/fmt__preference_emphasis.snap"))]
127 #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/snapshots/doc/fmt__preference_color.snap"))]
131 pub fn set_color_mode(mode: ColorMode) {
133 COLOR_OVERRIDE.store(mode);
134 }
135}
136
137#[derive(Copy, Clone, Debug, Eq, PartialEq)]
138pub(crate) enum Color {
139 Black,
140 Red,
141}
142
143impl Color {
144 const fn digit(self) -> u8 {
145 match self {
146 Self::Black => b'0',
147 Self::Red => b'1',
148 }
149 }
150}
151
152#[derive(Copy, Clone, Debug, Eq, PartialEq)]
153pub(crate) struct Foreground {
154 color: Color,
155 bright: bool,
156}
157
158impl Foreground {
159 fn start_ansi(self, sequence: &mut ControlSequence) -> fmt::Result {
160 let Self { color, bright } = self;
161
162 let buffer = &[if bright { b'9' } else { b'3' }, color.digit()];
163 let control = core::str::from_utf8(buffer).expect("should be valid utf-8 buffer");
165
166 sequence.push_control(control)
167 }
168
169 fn end_ansi(sequence: &mut ControlSequence) -> fmt::Result {
170 sequence.push_control("39")
171 }
172}
173
174struct ControlSequence<'a, 'b> {
175 fmt: &'a mut Formatter<'b>,
176 empty: bool,
177}
178
179impl<'a, 'b> ControlSequence<'a, 'b> {
180 fn new(fmt: &'a mut Formatter<'b>) -> Self {
181 Self { fmt, empty: true }
182 }
183
184 fn finish(self) -> Result<&'a mut Formatter<'b>, fmt::Error> {
185 if !self.empty {
186 self.fmt.write_str("m")?;
188 }
189
190 Ok(self.fmt)
191 }
192}
193
194impl ControlSequence<'_, '_> {
195 fn push_control(&mut self, control: &str) -> fmt::Result {
196 if self.empty {
197 self.fmt.write_str("\u{1b}[")?;
198 } else {
199 self.fmt.write_str(";")?;
200 }
201
202 self.fmt.write_str(control)?;
203 self.empty = false;
204
205 Ok(())
206 }
207}
208
209#[derive(Copy, Clone, Debug, Eq, PartialEq)]
210pub(crate) struct DisplayStyle {
211 bold: bool,
212 italic: bool,
213}
214
215impl DisplayStyle {
216 pub(crate) const fn new() -> Self {
217 Self {
218 bold: false,
219 italic: false,
220 }
221 }
222
223 pub(crate) fn set_bold(&mut self, value: bool) {
224 self.bold = value;
225 }
226
227 pub(crate) fn with_bold(mut self, value: bool) -> Self {
228 self.set_bold(value);
229 self
230 }
231
232 pub(crate) fn set_italic(&mut self, value: bool) {
233 self.italic = value;
234 }
235
236 pub(crate) fn with_italic(mut self, value: bool) -> Self {
237 self.set_italic(value);
238 self
239 }
240}
241
242impl DisplayStyle {
243 fn start_ansi(self, sequence: &mut ControlSequence) -> fmt::Result {
244 if self.bold {
245 sequence.push_control("1")?;
246 }
247
248 if self.italic {
249 sequence.push_control("3")?;
250 }
251
252 Ok(())
253 }
254
255 fn end_ansi(self, sequence: &mut ControlSequence) -> fmt::Result {
256 if self.bold {
257 sequence.push_control("22")?;
258 }
259
260 if self.italic {
261 sequence.push_control("23")?;
262 }
263
264 Ok(())
265 }
266}
267
268#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
269pub(crate) struct Style {
270 display: Option<DisplayStyle>,
271 foreground: Option<Foreground>,
272}
273
274impl Style {
275 pub(crate) const fn new() -> Self {
276 Self {
277 display: None,
278 foreground: None,
279 }
280 }
281
282 pub(crate) const fn apply<T: Display>(self, value: &T) -> StyleDisplay<T> {
283 StyleDisplay { style: self, value }
284 }
285
286 pub(crate) fn set_foreground(&mut self, color: Color, bright: bool) {
287 self.foreground = Some(Foreground { color, bright });
288 }
289
290 pub(crate) fn set_display(&mut self, style: DisplayStyle) {
291 self.display = Some(style);
292 }
293}
294
295pub(crate) struct StyleDisplay<'a, T: Display> {
296 style: Style,
297 value: &'a T,
298}
299
300impl<'a, T: Display> Display for StyleDisplay<'a, T> {
301 fn fmt(&self, mut f: &mut Formatter<'_>) -> fmt::Result {
302 let mut sequence = ControlSequence::new(f);
303
304 if let Some(display) = self.style.display {
305 display.start_ansi(&mut sequence)?;
306 }
307
308 if let Some(foreground) = self.style.foreground {
309 foreground.start_ansi(&mut sequence)?;
310 }
311
312 f = sequence.finish()?;
313
314 Display::fmt(&self.value, f)?;
315
316 let mut sequence = ControlSequence::new(f);
317
318 if let Some(display) = self.style.display {
319 display.end_ansi(&mut sequence)?;
320 }
321
322 if self.style.foreground.is_some() {
323 Foreground::end_ansi(&mut sequence)?;
324 }
325
326 sequence.finish()?;
327
328 Ok(())
329 }
330}