use std::fmt::{self, Display};
pub use owo_colors::Stream;
use owo_colors::{OwoColorize, Style};
const ACCENT: Style = Style::new().cyan();
const SUCCESS: Style = Style::new().green();
const ERROR: Style = Style::new().red();
const WARN: Style = Style::new().yellow();
const MUTED: Style = Style::new().dimmed();
const EMPHASIS: Style = Style::new().bold();
#[derive(Clone, Debug)]
pub struct Styled<T> {
value: T,
style: Style,
stream: Stream,
}
impl<T> Styled<T> {
const fn new(value: T, style: Style, stream: Stream) -> Self {
Self {
value,
style,
stream,
}
}
#[must_use]
pub const fn for_stderr(mut self) -> Self {
self.stream = Stream::Stderr;
self
}
#[must_use]
pub const fn for_stdout(mut self) -> Self {
self.stream = Stream::Stdout;
self
}
}
impl<T: Display> Display for Styled<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
self.value
.if_supports_color(self.stream, |v| v.style(self.style))
)
}
}
pub trait Stylize: Display {
fn accent(&self) -> Styled<&Self> {
Styled::new(self, ACCENT, Stream::Stdout)
}
fn success(&self) -> Styled<&Self> {
Styled::new(self, SUCCESS, Stream::Stdout)
}
fn error(&self) -> Styled<&Self> {
Styled::new(self, ERROR, Stream::Stderr)
}
fn warn(&self) -> Styled<&Self> {
Styled::new(self, WARN, Stream::Stderr)
}
fn muted(&self) -> Styled<&Self> {
Styled::new(self, MUTED, Stream::Stdout)
}
fn emphasis(&self) -> Styled<&Self> {
Styled::new(self, EMPHASIS, Stream::Stdout)
}
}
impl<T: Display + ?Sized> Stylize for T {}
pub const CHECK: &str = "✓";
pub const CROSS: &str = "✗";
pub const ARROW: &str = "→";
pub const BULLET: &str = "○";
pub const CURRENT: &str = "@";
pub const PIPE: &str = "│";
pub const UP_ARROW: &str = "↑";
#[inline]
pub const fn check() -> Styled<&'static str> {
Styled::new(CHECK, SUCCESS, Stream::Stdout)
}
#[inline]
pub const fn cross() -> Styled<&'static str> {
Styled::new(CROSS, ERROR, Stream::Stderr)
}
#[inline]
pub const fn arrow() -> Styled<&'static str> {
Styled::new(ARROW, ACCENT, Stream::Stdout)
}
#[inline]
pub const fn bullet() -> Styled<&'static str> {
Styled::new(BULLET, MUTED, Stream::Stdout)
}
#[inline]
pub const fn pipe() -> Styled<&'static str> {
Styled::new(PIPE, MUTED, Stream::Stdout)
}
#[inline]
pub const fn up_arrow() -> Styled<&'static str> {
Styled::new(UP_ARROW, WARN, Stream::Stdout)
}
const fn to_hyperlink_stream(stream: Stream) -> supports_hyperlinks::Stream {
match stream {
Stream::Stdout => supports_hyperlinks::Stream::Stdout,
Stream::Stderr => supports_hyperlinks::Stream::Stderr,
}
}
pub fn hyperlink_url(stream: Stream, url: &str) -> String {
if supports_hyperlinks::on(to_hyperlink_stream(stream)) {
terminal_link::Link::new(url, url).to_string()
} else {
url.to_string()
}
}
use indicatif::ProgressStyle;
use std::sync::OnceLock;
pub fn spinner_style() -> ProgressStyle {
static STYLE: OnceLock<ProgressStyle> = OnceLock::new();
STYLE
.get_or_init(|| {
ProgressStyle::default_spinner()
.template("{spinner:.cyan} {msg}")
.expect("hardcoded spinner template is valid")
.tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏")
})
.clone()
}