use core::fmt::{self, Display, Formatter};
use crate::{
Report,
fmt::r#override::{AtomicOverride, AtomicPreference},
};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
pub enum ColorMode {
None,
Color,
#[default]
Emphasis,
}
impl ColorMode {
pub(super) fn load() -> Self {
COLOR_OVERRIDE.load()
}
}
impl AtomicPreference for ColorMode {
fn from_u8(value: u8) -> Self {
match value {
0x00 => Self::None,
0x01 => Self::Color,
0x02 => Self::Emphasis,
_ => Self::default(),
}
}
fn into_u8(self) -> u8 {
match self {
Self::None => 0x00,
Self::Color => 0x01,
Self::Emphasis => 0x02,
}
}
}
static COLOR_OVERRIDE: AtomicOverride<ColorMode> = AtomicOverride::new();
impl Report<()> {
#[cfg_attr(doc, doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/snapshots/doc/fmt__preference_none.snap")))]
#[cfg_attr(doc, doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/snapshots/doc/fmt__preference_emphasis.snap")))]
#[cfg_attr(doc, doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/snapshots/doc/fmt__preference_color.snap")))]
pub fn set_color_mode(mode: ColorMode) {
COLOR_OVERRIDE.store(mode);
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum Color {
Black,
Red,
}
impl Color {
const fn digit(self) -> u8 {
match self {
Self::Black => b'0',
Self::Red => b'1',
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) struct Foreground {
color: Color,
bright: bool,
}
impl Foreground {
fn start_ansi(self, sequence: &mut ControlSequence) -> fmt::Result {
let Self { color, bright } = self;
let buffer = &[if bright { b'9' } else { b'3' }, color.digit()];
let control = core::str::from_utf8(buffer).expect("should be valid utf-8 buffer");
sequence.push_control(control)
}
fn end_ansi(sequence: &mut ControlSequence) -> fmt::Result {
sequence.push_control("39")
}
}
struct ControlSequence<'a, 'b> {
fmt: &'a mut Formatter<'b>,
empty: bool,
}
impl<'a, 'b> ControlSequence<'a, 'b> {
const fn new(fmt: &'a mut Formatter<'b>) -> Self {
Self { fmt, empty: true }
}
fn finish(self) -> Result<&'a mut Formatter<'b>, fmt::Error> {
if !self.empty {
self.fmt.write_str("m")?;
}
Ok(self.fmt)
}
}
impl ControlSequence<'_, '_> {
fn push_control(&mut self, control: &str) -> fmt::Result {
if self.empty {
self.fmt.write_str("\u{1b}[")?;
} else {
self.fmt.write_str(";")?;
}
self.fmt.write_str(control)?;
self.empty = false;
Ok(())
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) struct DisplayStyle {
bold: bool,
italic: bool,
}
impl DisplayStyle {
pub(crate) const fn new() -> Self {
Self {
bold: false,
italic: false,
}
}
pub(crate) const fn set_bold(&mut self, value: bool) {
self.bold = value;
}
pub(crate) const fn with_bold(mut self, value: bool) -> Self {
self.set_bold(value);
self
}
pub(crate) const fn set_italic(&mut self, value: bool) {
self.italic = value;
}
pub(crate) const fn with_italic(mut self, value: bool) -> Self {
self.set_italic(value);
self
}
}
impl DisplayStyle {
fn start_ansi(self, sequence: &mut ControlSequence) -> fmt::Result {
if self.bold {
sequence.push_control("1")?;
}
if self.italic {
sequence.push_control("3")?;
}
Ok(())
}
fn end_ansi(self, sequence: &mut ControlSequence) -> fmt::Result {
if self.bold {
sequence.push_control("22")?;
}
if self.italic {
sequence.push_control("23")?;
}
Ok(())
}
}
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) struct Style {
display: Option<DisplayStyle>,
foreground: Option<Foreground>,
}
impl Style {
pub(crate) const fn new() -> Self {
Self {
display: None,
foreground: None,
}
}
pub(crate) const fn apply<T: Display>(self, value: &T) -> StyleDisplay<'_, T> {
StyleDisplay { style: self, value }
}
pub(crate) const fn set_foreground(&mut self, color: Color, bright: bool) {
self.foreground = Some(Foreground { color, bright });
}
pub(crate) const fn set_display(&mut self, style: DisplayStyle) {
self.display = Some(style);
}
}
pub(crate) struct StyleDisplay<'a, T: Display> {
style: Style,
value: &'a T,
}
impl<T: Display> Display for StyleDisplay<'_, T> {
fn fmt(&self, mut fmt: &mut Formatter<'_>) -> fmt::Result {
let mut sequence = ControlSequence::new(fmt);
if let Some(display) = self.style.display {
display.start_ansi(&mut sequence)?;
}
if let Some(foreground) = self.style.foreground {
foreground.start_ansi(&mut sequence)?;
}
fmt = sequence.finish()?;
Display::fmt(&self.value, fmt)?;
let mut sequence = ControlSequence::new(fmt);
if let Some(display) = self.style.display {
display.end_ansi(&mut sequence)?;
}
if self.style.foreground.is_some() {
Foreground::end_ansi(&mut sequence)?;
}
sequence.finish()?;
Ok(())
}
}