bird_chat/
formatting.rs

1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::fmt::{Display, Formatter};
4
5type HexColorInner<'a> = either::Either<(u8, u8, u8), Cow<'a, str>>;
6
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub enum Decoration {
9    Random,
10    Bold,
11    Strikethrough,
12    Underlined,
13    Italic,
14}
15
16#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
17#[serde(try_from = "String", into = "String")]
18pub struct HexColor<'a>(HexColorInner<'a>);
19
20#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
21#[serde(rename_all = "snake_case")]
22pub enum DefaultColor {
23    Black,
24    DarkBlue,
25    DarkGreen,
26    DarkCyan,
27    DarkRed,
28    Purple,
29    Gold,
30    Gray,
31    DarkGray,
32    Blue,
33    BrightGreen,
34    Cyan,
35    Red,
36    Pink,
37    Yellow,
38    White,
39}
40
41#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
42#[serde(untagged)]
43pub enum Color<'a> {
44    Default(DefaultColor),
45    Hex(HexColor<'a>),
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
49pub enum HexColorError {
50    #[error("Hex value contains bad characters")]
51    HexValueContainsBadCharacters,
52    #[error("Hex value too long")]
53    HexValueTooLong,
54    #[error("Hex value too small")]
55    HexValueTooSmall,
56}
57
58impl<'a> HexColor<'a> {
59    const fn new(inner: HexColorInner<'a>) -> Self {
60        Self(inner)
61    }
62
63    const fn get(&self) -> &HexColorInner<'a> {
64        &self.0
65    }
66
67    pub const fn new_rgb(r: u8, g: u8, b: u8) -> Self {
68        Self::new(either::Either::Left((r, g, b)))
69    }
70
71    pub fn new_hex(hex: impl Into<Cow<'a, str>>) -> Result<Self, HexColorError> {
72        let hex = hex.into();
73        match hex.len().cmp(&7) {
74            Ordering::Less => Err(HexColorError::HexValueTooLong),
75            Ordering::Greater => Err(HexColorError::HexValueTooLong),
76            // Safety. The length is 7 so next will get first element, which is exist
77            Ordering::Equal => match unsafe { hex.chars().next().unwrap_unchecked() } == '#' &&
78                hex[1..=7].contains(|c: char| {
79                    match c {
80                        '0'..='9' | 'a'..='f' | 'A'..='F' => false,
81                        _ => true
82                    }
83                }) {
84                true => Err(HexColorError::HexValueContainsBadCharacters),
85                false => Ok(Self::new(HexColorInner::Right(hex)))
86            }
87        }
88    }
89
90    pub fn get_rgb(&self) -> (u8, u8, u8) {
91        match self.get() {
92            HexColorInner::Left((r, g, b)) => (*r, *g, *b),
93            // Safety. Guarantied by constructors
94            HexColorInner::Right(str) => unsafe {
95                (
96                    u8::from_str_radix(&str[1..3], 16).unwrap_unchecked(),
97                    u8::from_str_radix(&str[3..5], 16).unwrap_unchecked(),
98                    u8::from_str_radix(&str[5..7], 16).unwrap_unchecked(),
99                )
100            }
101        }
102    }
103
104    pub fn get_hex(&'a self) -> Cow<'a, str> {
105        match self.get() {
106            HexColorInner::Left((r, g, b)) =>
107                Cow::Owned(format!("#{:02x}{:02x}{:02x}", r, g, b)),
108            HexColorInner::Right(str) => Cow::Borrowed(&str)
109        }
110    }
111}
112
113impl Display for HexColor<'_> {
114    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
115        write!(f, "{}", self.get_hex())
116    }
117}
118
119impl TryFrom<String> for HexColor<'_> {
120    type Error = HexColorError;
121
122    fn try_from(value: String) -> Result<Self, Self::Error> {
123        HexColor::new_hex(value)
124    }
125}
126
127impl From<HexColor<'_>> for String {
128    fn from(color: HexColor<'_>) -> Self {
129        color.to_string()
130    }
131}
132
133impl From<DefaultColor> for Color<'_> {
134    fn from(default_color: DefaultColor) -> Self {
135        Color::Default(default_color)
136    }
137}
138
139impl<'a> From<HexColor<'a>> for Color<'a> {
140    fn from(hex_color: HexColor<'a>) -> Self {
141        Color::Hex(hex_color)
142    }
143}