use std::io::IsTerminal as _;
use std::os::fd::{AsRawFd, BorrowedFd};
use std::sync::atomic::{AtomicBool, AtomicI32};
use std::{fmt, sync};
use once_cell::sync::Lazy;
use super::color::Color;
use super::style::{Property, Style};
static TERMINAL: AtomicI32 = AtomicI32::new(libc::STDOUT_FILENO);
static ENABLED: AtomicBool = AtomicBool::new(true);
static FORCED: AtomicBool = AtomicBool::new(false);
#[derive(Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)]
pub struct Paint<T> {
pub item: T,
pub style: Style,
}
impl Paint<&str> {
pub fn content(&self) -> &str {
self.item
}
}
impl Paint<String> {
pub fn content(&self) -> &str {
self.item.as_str()
}
}
impl<T> From<T> for Paint<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl From<&str> for Paint<String> {
fn from(item: &str) -> Self {
Self::new(item.to_string())
}
}
impl From<Paint<&str>> for Paint<String> {
fn from(paint: Paint<&str>) -> Self {
Self {
item: paint.item.to_owned(),
style: paint.style,
}
}
}
impl From<Paint<String>> for String {
fn from(value: Paint<String>) -> Self {
value.item
}
}
impl<T> Paint<T> {
#[inline]
pub const fn new(item: T) -> Paint<T> {
Paint {
item,
style: Style {
foreground: Color::Unset,
background: Color::Unset,
properties: Property::new(),
wrap: false,
},
}
}
#[inline]
pub const fn wrapping(item: T) -> Paint<T> {
Paint::new(item).wrap()
}
#[inline]
pub const fn rgb(r: u8, g: u8, b: u8, item: T) -> Paint<T> {
Paint::new(item).fg(Color::RGB(r, g, b))
}
#[inline]
pub const fn fixed(color: u8, item: T) -> Paint<T> {
Paint::new(item).fg(Color::Fixed(color))
}
pub const fn red(item: T) -> Paint<T> {
Paint::new(item).fg(Color::Red)
}
pub const fn black(item: T) -> Paint<T> {
Paint::new(item).fg(Color::Black)
}
pub const fn yellow(item: T) -> Paint<T> {
Paint::new(item).fg(Color::Yellow)
}
pub const fn green(item: T) -> Paint<T> {
Paint::new(item).fg(Color::Green)
}
pub const fn cyan(item: T) -> Paint<T> {
Paint::new(item).fg(Color::Cyan)
}
pub const fn blue(item: T) -> Paint<T> {
Paint::new(item).fg(Color::Blue)
}
pub const fn magenta(item: T) -> Paint<T> {
Paint::new(item).fg(Color::Magenta)
}
pub const fn white(item: T) -> Paint<T> {
Paint::new(item).fg(Color::White)
}
#[inline]
pub const fn style(&self) -> Style {
self.style
}
#[inline]
pub const fn inner(&self) -> &T {
&self.item
}
#[inline]
pub fn with_style(mut self, style: Style) -> Paint<T> {
self.style = style;
self
}
#[inline]
pub const fn wrap(mut self) -> Paint<T> {
self.style.wrap = true;
self
}
#[inline]
pub const fn fg(mut self, color: Color) -> Paint<T> {
self.style.foreground = color;
self
}
#[inline]
pub const fn bg(mut self, color: Color) -> Paint<T> {
self.style.background = color;
self
}
pub fn bold(mut self) -> Self {
self.style.properties.set(Property::BOLD);
self
}
pub fn dim(mut self) -> Self {
self.style.properties.set(Property::DIM);
self
}
pub fn italic(mut self) -> Self {
self.style.properties.set(Property::ITALIC);
self
}
pub fn underline(mut self) -> Self {
self.style.properties.set(Property::UNDERLINE);
self
}
pub fn invert(mut self) -> Self {
self.style.properties.set(Property::INVERT);
self
}
pub fn strikethrough(mut self) -> Self {
self.style.properties.set(Property::STRIKETHROUGH);
self
}
pub fn blink(mut self) -> Self {
self.style.properties.set(Property::BLINK);
self
}
pub fn hidden(mut self) -> Self {
self.style.properties.set(Property::HIDDEN);
self
}
}
impl<T: fmt::Display> fmt::Display for Paint<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if Paint::is_enabled() && self.style.wrap {
let mut prefix = String::new();
prefix.push_str("\x1B[0m");
self.style.fmt_prefix(&mut prefix)?;
self.style.fmt_prefix(f)?;
let item = format!("{}", self.item).replace("\x1B[0m", &prefix);
fmt::Display::fmt(&item, f)?;
self.style.fmt_suffix(f)
} else if Paint::is_enabled() {
self.style.fmt_prefix(f)?;
fmt::Display::fmt(&self.item, f)?;
self.style.fmt_suffix(f)
} else {
fmt::Display::fmt(&self.item, f)
}
}
}
impl Paint<()> {
pub fn is_enabled() -> bool {
if FORCED.load(sync::atomic::Ordering::SeqCst) {
return true;
}
let clicolor = anstyle_query::clicolor();
let clicolor_enabled = clicolor.unwrap_or(false);
let clicolor_disabled = !clicolor.unwrap_or(true);
let terminal = TERMINAL.load(sync::atomic::Ordering::SeqCst);
let is_terminal = unsafe { BorrowedFd::borrow_raw(terminal).is_terminal() };
let is_enabled = ENABLED.load(sync::atomic::Ordering::SeqCst);
is_terminal
&& is_enabled
&& !anstyle_query::no_color()
&& !clicolor_disabled
&& (anstyle_query::term_supports_color() || clicolor_enabled || anstyle_query::is_ci())
|| anstyle_query::clicolor_force()
}
pub fn truecolor() -> bool {
static TRUECOLOR: Lazy<bool> = Lazy::new(anstyle_query::term_supports_color);
*TRUECOLOR
}
pub fn enable() {
ENABLED.store(true, sync::atomic::Ordering::SeqCst);
}
pub fn set_terminal(fd: impl AsRawFd) {
TERMINAL.store(fd.as_raw_fd(), sync::atomic::Ordering::SeqCst);
}
pub fn force(force: bool) {
FORCED.store(force, sync::atomic::Ordering::SeqCst);
}
pub fn disable() {
ENABLED.store(false, sync::atomic::Ordering::SeqCst);
}
}
#[derive(Debug, Clone)]
pub struct Filled<T> {
pub item: T,
pub color: Color,
}
impl<T: fmt::Display> fmt::Display for Filled<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", Paint::wrapping(&self.item).bg(self.color))
}
}
pub fn paint<T>(item: T) -> Paint<T> {
Paint::new(item)
}