style_term/
lib.rs

1pub mod no_color;
2pub mod styles;
3pub mod color;
4
5use std::borrow::{Cow};
6use std::error::Error;
7use std::fmt::{Debug, Display, Formatter};
8pub use crate::color::{DefaultColor, Color, DisplayColor, EightBitColor, TrueColor};
9pub use styles::{Styles, Style};
10
11/// A container for all the styles applied to the String
12/// This can also be serialized and applied with serde
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[derive(Debug, Clone, Default)]
15pub struct StylesContainer {
16    pub text_color: Option<Color>,
17    pub background_color: Option<Color>,
18    #[cfg_attr(feature = "serde", serde(default))]
19    pub styles: Vec<Style>,
20}
21
22
23#[derive(Debug)]
24pub struct InvalidStyleError;
25
26impl Display for InvalidStyleError {
27    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
28        writeln!(f, "The Value provided is not compatible with ANSI escape code standards")
29    }
30}
31
32impl Error for InvalidStyleError {}
33
34/// A Styled String.
35/// This is the only safe Display method in terms of supporting NO_COLOR
36#[derive(Debug, Clone)]
37pub struct StyledString<'content> {
38    /// Content of the String
39    content: &'content str,
40    styles: Cow<'content, StylesContainer>,
41}
42
43impl<'content> Display for StyledString<'content> {
44    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45        if !no_color::show_styles() {
46            return Display::fmt(&self.content, f);
47        }
48        for style in &self.styles.styles {
49            Display::fmt(&style, f)?;
50        }
51        if let Some(background) = self.styles.background_color.as_ref() {
52            background.fmt_background(f)?;
53        }
54        if let Some(color) = self.styles.text_color.as_ref() {
55            color.fmt_color(f)?;
56        }
57        write!(f, "{}{}", self.content, styles::RESET)
58    }
59}
60
61impl<'content> StyledString<'content> {
62    pub(crate) fn new(content: &'content str) -> StyledString {
63        StyledString {
64            content,
65            styles: Cow::Owned(StylesContainer::default()),
66        }
67    }
68    pub fn text_color<C: Into<Color>>(mut self, text: C) -> Self {
69        self.styles.to_mut().text_color = Some(text.into());
70        self
71    }
72    pub fn background_color<C: Into<Color>>(mut self, text: C) -> Self {
73        self.styles.to_mut().background_color = Some(text.into());
74        self
75    }
76
77    pub fn add_style<S: Into<Style>>(mut self, style: S) -> Self {
78        self.styles.to_mut().styles.push(style.into());
79        self
80    }
81}
82
83/// A trait meaning it can be converted into a styled string instead
84pub trait StyleString {
85    /// Converts the current type into a Styled String
86    fn style(&self) -> StyledString;
87
88    /// Creates a styled string with the applied settings
89    fn apply_styles<'content>(&'content self, styles: &'content StylesContainer) -> StyledString<'content> {
90        let mut styled = self.style();
91        styled.styles = Cow::Borrowed(styles);
92        styled
93    }
94}
95
96impl StyleString for String {
97    fn style(&self) -> StyledString {
98        StyledString::new(self.as_str())
99    }
100}
101
102impl<'content> StyleString for &'content String {
103    fn style(&self) -> StyledString<'content> {
104        StyledString::new(self.as_str())
105    }
106}
107
108impl<'content> StyleString for &'content str {
109    fn style(&self) -> StyledString {
110        StyledString::new(self)
111    }
112}
113
114
115#[cfg(test)]
116pub mod test {
117    use std::fs::File;
118    use std::io::Write;
119    use crate::{Color, DefaultColor, EightBitColor, StyleString, TrueColor};
120    use crate::color::FourBitColor;
121    use crate::styles::Styles;
122
123    #[test]
124    pub fn general_test() {
125        println!("{}", "Bright Blue BG: Black Bold".style().text_color(DefaultColor::BrightBlue).background_color(FourBitColor::Black).add_style(Styles::Bold));
126        println!("{}", "Bright Blue BG: Black Italic".style().text_color(DefaultColor::BrightBlue).background_color(FourBitColor::Black).add_style(Styles::Italic));
127        println!("{}", "Bright Blue BG: Black Reset".style().text_color(DefaultColor::BrightBlue).background_color(FourBitColor::Black).add_style(Styles::Reset));
128        println!("{}", "Bright Blue BG: Black Underline".style().text_color(DefaultColor::BrightBlue).background_color(FourBitColor::Black).add_style(Styles::Underline));
129        println!("{}", "Bright Blue BG: Black".style().text_color(DefaultColor::BrightBlue).background_color(FourBitColor::Black));
130    }
131
132    #[test]
133    pub fn serde_test() {
134        let true_color = Color::TrueColor(TrueColor::from((5, 5, 5)));
135        let eight_bit_color = Color::EightBitColor(EightBitColor::from(5));
136        let four_bit_color = Color::FourBitColor(FourBitColor::Black);
137        println!("true_color: {}", serde_json::to_string_pretty(&true_color).unwrap());
138        println!("eight_bit_color: {}", serde_json::to_string_pretty(&eight_bit_color).unwrap());
139        println!("for_bit_color: {}", serde_json::to_string_pretty(&four_bit_color).unwrap());
140    }
141}