use core::fmt;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::{string::{String, ToString}, borrow::Cow};
#[cfg(feature = "std")]
use std::borrow::Cow;
use crate::{Color, Attribute, Quirk, Style, Condition};
#[derive(Copy, Clone)]
pub struct Painted<T> {
pub value: T,
pub style: Style,
}
pub trait Paint {
#[inline(always)]
fn new(self) -> Painted<Self> where Self: Sized {
Painted::new(self)
}
#[doc(hidden)]
#[inline(always)]
fn apply(&self, a: crate::style::Application) -> Painted<&Self> {
Painted::new(self).apply(a)
}
#[inline(always)]
fn paint<S: Into<Style>>(&self, style: S) -> Painted<&Self> {
Painted { value: self, style: style.into() }
}
properties!(signature(&Self) -> Painted<&Self>);
}
#[allow(rustdoc::broken_intra_doc_links)]
impl<T: ?Sized> Paint for T {
properties!(constructor(&Self) -> Painted<&Self>);
}
impl<T> Painted<T> {
#[inline(always)]
pub const fn new(value: T) -> Painted<T> {
Painted { value, style: Style::new() }
}
#[inline(always)]
const fn apply(mut self, a: crate::style::Application) -> Self {
self.style = self.style.apply(a);
self
}
#[inline]
pub(crate) fn enabled(&self) -> bool {
crate::is_enabled() && self.style.condition.map_or(true, |c| c())
}
properties!([pub const] constructor(Self) -> Self);
}
impl<T> Painted<T> {
pub(crate) fn color_fmt_value(
&self,
fmt: &dyn Fn(&T, &mut fmt::Formatter) -> fmt::Result,
f: &mut fmt::Formatter,
) -> fmt::Result {
self.style.fmt_prefix(f)?;
fmt(&self.value, f)?;
self.style.fmt_suffix(f)
}
#[cfg(feature = "alloc")]
pub(crate) fn reset_fmt_args(
&self,
fmt: &dyn Fn(&T, &mut fmt::Formatter) -> fmt::Result,
f: &mut fmt::Formatter,
args: &fmt::Arguments<'_>,
) -> fmt::Result {
enum State { Searching, Open, }
let mut state = State::Searching;
let escape = |c: char| {
match state {
State::Searching if c == '\x1B' => { state = State::Open; true }
State::Open if c == 'm' => { state = State::Searching; true }
State::Searching => false,
State::Open => true,
}
};
let string = args.as_str()
.map(|string| Cow::Borrowed(string))
.unwrap_or_else(|| Cow::Owned(args.to_string()));
if string.contains('\x1B') {
f.write_str(&string.replace(escape, ""))
} else {
fmt(&self.value, f)
}
}
#[cfg(feature = "alloc")]
pub(crate) fn color_wrap_fmt_args(
&self,
fmt: &dyn Fn(&T, &mut fmt::Formatter) -> fmt::Result,
f: &mut fmt::Formatter,
args: &fmt::Arguments<'_>,
) -> fmt::Result {
let string = args.as_str()
.map(|string| Cow::Borrowed(string))
.unwrap_or_else(|| Cow::Owned(args.to_string()));
if !string.contains('\x1B') {
return self.color_fmt_value(fmt, f);
}
let mut prefix = String::new();
prefix.push_str("\x1B[0m");
self.style.fmt_prefix(&mut prefix)?;
self.style.fmt_prefix(f)?;
write!(f, "{}", string.replace("\x1B[0m", &prefix))?;
self.style.fmt_suffix(f)
}
pub(crate) fn fmt_args(
&self,
fmt: &dyn Fn(&T, &mut fmt::Formatter) -> fmt::Result,
f: &mut fmt::Formatter,
_args: fmt::Arguments<'_>,
) -> fmt::Result {
let enabled = self.enabled();
let masked = self.style.quirks.contains(Quirk::Mask);
#[cfg(not(feature = "alloc"))]
match (enabled, masked) {
(true, _) => self.color_fmt_value(fmt, f),
(false, false) => fmt(&self.value, f),
(false, true) => Ok(()),
}
#[cfg(feature = "alloc")]
match (enabled, masked, self.style.quirks.contains(Quirk::Wrap)) {
(true, _, true) => self.color_wrap_fmt_args(fmt, f, &_args),
(true, _, false) => self.color_fmt_value(fmt, f),
(false, false, true) => self.reset_fmt_args(fmt, f, &_args),
(false, false, false) => fmt(&self.value, f),
(false, true, _) => Ok(()),
}
}
}
impl_fmt_traits!(<T> Painted<T> => self.value (T));
impl<T> From<Painted<T>> for Style {
fn from(painted: Painted<T>) -> Self {
painted.style
}
}