sticknodes_rs/
color.rs

1use serde::Deserialize;
2use serde::Serialize;
3extern crate alloc;
4use alloc::{borrow::ToOwned, format, string::String};
5
6use crate::ColorError;
7
8#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
9pub struct Color {
10    pub alpha: u8,
11    pub blue: u8,
12    pub green: u8,
13    pub red: u8,
14}
15
16impl Color {
17    /// Creates a new color from RGBA values.
18    pub fn from_rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
19        Color {
20            red,
21            green,
22            blue,
23            alpha,
24        }
25    }
26
27    /// Creates a new color from RGB values (assumes alpha is 255).
28    pub fn from_rgb(red: u8, green: u8, blue: u8) -> Self {
29        Color {
30            red,
31            green,
32            blue,
33            alpha: 255,
34        }
35    }
36
37    /// Creates a new color from a hex string.
38    /// The string can be in the formats: "#RGB", "#RGBA", "#RRGGBB", "#RRGGBBAA", "RGB", "RGBA", "RRGGBB", or "RRGGBBAA" (case-insensitive). Any other format will fail.
39    pub fn from_hex(hex: &str) -> Result<Self, ColorError> {
40        if hex.len() == 0 {
41            return Err(ColorError::EmptyHexString());
42        }
43
44        let trimmed_hex = hex.trim_start_matches('#');
45
46        match trimmed_hex.len() {
47            0 => return Err(ColorError::EmptyTrimmedHexString()),
48            3 => {
49                let red = u8::from_str_radix(&trimmed_hex[0..1], 16).map_err(|_| {
50                    ColorError::InvalidHexStringValue(
51                        format!("#{}", trimmed_hex),
52                        trimmed_hex[0..1].to_owned(),
53                    )
54                })? * 17; // multiply by 17 to turn value of single hex digit to the value of if it was repeated, ex: "f" -> "ff"
55                let green = u8::from_str_radix(&trimmed_hex[1..2], 16).map_err(|_| {
56                    ColorError::InvalidHexStringValue(
57                        format!("#{}", trimmed_hex),
58                        trimmed_hex[1..2].to_owned(),
59                    )
60                })? * 17;
61                let blue = u8::from_str_radix(&trimmed_hex[2..3], 16).map_err(|_| {
62                    ColorError::InvalidHexStringValue(
63                        format!("#{}", trimmed_hex),
64                        trimmed_hex[2..3].to_owned(),
65                    )
66                })? * 17;
67                Ok(Color {
68                    red,
69                    green,
70                    blue,
71                    alpha: 255,
72                })
73            }
74            4 => {
75                let red = u8::from_str_radix(&trimmed_hex[0..1], 16).map_err(|_| {
76                    ColorError::InvalidHexStringValue(
77                        format!("#{}", trimmed_hex),
78                        trimmed_hex[0..1].to_owned(),
79                    )
80                })? * 17;
81                let green = u8::from_str_radix(&trimmed_hex[1..2], 16).map_err(|_| {
82                    ColorError::InvalidHexStringValue(
83                        format!("#{}", trimmed_hex),
84                        trimmed_hex[1..2].to_owned(),
85                    )
86                })? * 17;
87                let blue = u8::from_str_radix(&trimmed_hex[2..3], 16).map_err(|_| {
88                    ColorError::InvalidHexStringValue(
89                        format!("#{}", trimmed_hex),
90                        trimmed_hex[2..3].to_owned(),
91                    )
92                })? * 17;
93                let alpha = u8::from_str_radix(&trimmed_hex[3..4], 16).map_err(|_| {
94                    ColorError::InvalidHexStringValue(
95                        format!("#{}", trimmed_hex),
96                        trimmed_hex[3..4].to_owned(),
97                    )
98                })? * 17;
99                Ok(Color {
100                    red,
101                    green,
102                    blue,
103                    alpha,
104                })
105            }
106            6 => {
107                let red = u8::from_str_radix(&trimmed_hex[0..2], 16).map_err(|_| {
108                    ColorError::InvalidHexStringValue(
109                        format!("#{}", trimmed_hex),
110                        trimmed_hex[0..2].to_owned(),
111                    )
112                })?;
113                let green = u8::from_str_radix(&trimmed_hex[2..4], 16).map_err(|_| {
114                    ColorError::InvalidHexStringValue(
115                        format!("#{}", trimmed_hex),
116                        trimmed_hex[2..4].to_owned(),
117                    )
118                })?;
119                let blue = u8::from_str_radix(&trimmed_hex[4..6], 16).map_err(|_| {
120                    ColorError::InvalidHexStringValue(
121                        format!("#{}", trimmed_hex),
122                        trimmed_hex[4..6].to_owned(),
123                    )
124                })?;
125                Ok(Color {
126                    red,
127                    green,
128                    blue,
129                    alpha: 255,
130                })
131            }
132            8 => {
133                let red = u8::from_str_radix(&trimmed_hex[0..2], 16).map_err(|_| {
134                    ColorError::InvalidHexStringValue(
135                        format!("#{}", trimmed_hex),
136                        trimmed_hex[0..2].to_owned(),
137                    )
138                })?;
139                let green = u8::from_str_radix(&trimmed_hex[2..4], 16).map_err(|_| {
140                    ColorError::InvalidHexStringValue(
141                        format!("#{}", trimmed_hex),
142                        trimmed_hex[2..4].to_owned(),
143                    )
144                })?;
145                let blue = u8::from_str_radix(&trimmed_hex[4..6], 16).map_err(|_| {
146                    ColorError::InvalidHexStringValue(
147                        format!("#{}", trimmed_hex),
148                        trimmed_hex[4..6].to_owned(),
149                    )
150                })?;
151                let alpha = u8::from_str_radix(&trimmed_hex[6..8], 16).map_err(|_| {
152                    ColorError::InvalidHexStringValue(
153                        format!("#{}", trimmed_hex),
154                        trimmed_hex[6..8].to_owned(),
155                    )
156                })?;
157                Ok(Color {
158                    red,
159                    green,
160                    blue,
161                    alpha,
162                })
163            }
164            _ => Err(ColorError::InvalidHexStringLength(
165                trimmed_hex.to_owned(),
166                trimmed_hex.len(),
167            )),
168        }
169    }
170
171    /// Converts the color to a hex string.
172    pub fn to_hex(&self) -> String {
173        format!(
174            "#{:02X}{:02X}{:02X}{:02X}",
175            self.red, self.green, self.blue, self.alpha
176        )
177    }
178}
179
180/// Common color constants
181impl Color {
182    pub const RED: Self = Color {
183        red: 255,
184        green: 0,
185        blue: 0,
186        alpha: 255,
187    };
188    pub const GREEN: Self = Color {
189        red: 0,
190        green: 255,
191        blue: 0,
192        alpha: 255,
193    };
194    pub const BLUE: Self = Color {
195        red: 0,
196        green: 0,
197        blue: 255,
198        alpha: 255,
199    };
200    pub const WHITE: Self = Color {
201        red: 255,
202        green: 255,
203        blue: 255,
204        alpha: 255,
205    };
206    pub const BLACK: Self = Color {
207        red: 0,
208        green: 0,
209        blue: 0,
210        alpha: 255,
211    };
212}
213
214#[cfg(feature = "std")]
215impl std::fmt::Display for Color {
216    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217        write!(
218            f,
219            "rgba({}, {}, {}, {})",
220            self.red, self.green, self.blue, self.alpha
221        )
222    }
223}
224
225impl From<(u8, u8, u8)> for Color {
226    fn from(rgb: (u8, u8, u8)) -> Self {
227        Color::from_rgb(rgb.0, rgb.1, rgb.2)
228    }
229}
230
231impl From<(u8, u8, u8, u8)> for Color {
232    fn from(rgba: (u8, u8, u8, u8)) -> Self {
233        Color::from_rgba(rgba.0, rgba.1, rgba.2, rgba.3)
234    }
235}
236
237impl From<&str> for Color {
238    fn from(hex: &str) -> Self {
239        Color::from_hex(hex).unwrap_or_default()
240    }
241}
242
243impl From<u32> for Color {
244    fn from(hex: u32) -> Self {
245        let red = ((hex >> 24) & 0xFF) as u8;
246        let green = ((hex >> 16) & 0xFF) as u8;
247        let blue = ((hex >> 8) & 0xFF) as u8;
248        let alpha = (hex & 0xFF) as u8;
249        Color {
250            red,
251            green,
252            blue,
253            alpha,
254        }
255    }
256}