tiny_gradient/
rgb.rs

1//! This module contains [RGB] structure.
2
3use core::fmt::{self, Display};
4use core::{num::ParseIntError, str};
5
6/// Red Green Blue
7#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
8pub struct RGB<T = u8> {
9    /// Red
10    pub r: T,
11    /// Green
12    pub g: T,
13    /// Blue
14    pub b: T,
15}
16
17impl<T> RGB<T> {
18    /// Creates a new [RGB]
19    pub const fn new(r: T, g: T, b: T) -> Self {
20        Self { r, g, b }
21    }
22}
23
24impl From<(u8, u8, u8)> for RGB {
25    fn from((r, g, b): (u8, u8, u8)) -> Self {
26        Self { r, g, b }
27    }
28}
29
30impl str::FromStr for RGB {
31    type Err = ParseRGBError;
32
33    fn from_str(mut s: &str) -> Result<Self, Self::Err> {
34        if !s.is_ascii() {
35            return Err(ParseRGBError::new(RGBErrorKind::Invalid));
36        }
37
38        if let Some(stripped) = s.strip_prefix('#') {
39            s = stripped;
40        }
41
42        if s.len() != 6 {
43            return Err(ParseRGBError::new(RGBErrorKind::Size));
44        }
45
46        let r = u8::from_str_radix(&s[..2], 16)
47            .map_err(|e| ParseRGBError::new(RGBErrorKind::Format { error: e, pos: 0 }))?;
48
49        let g = u8::from_str_radix(&s[2..4], 16)
50            .map_err(|e| ParseRGBError::new(RGBErrorKind::Format { error: e, pos: 1 }))?;
51
52        let b = u8::from_str_radix(&s[4..6], 16)
53            .map_err(|e| ParseRGBError::new(RGBErrorKind::Format { error: e, pos: 2 }))?;
54
55        Ok(Self { r, g, b })
56    }
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct ParseRGBError {
61    kind: RGBErrorKind,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub enum RGBErrorKind {
66    Size,
67    Invalid,
68    Format { pos: usize, error: ParseIntError },
69}
70
71impl ParseRGBError {
72    fn new(kind: RGBErrorKind) -> Self {
73        Self { kind }
74    }
75
76    /// Outputs the detailed cause of parsing an integer failing.
77    #[must_use]
78    pub fn kind(&self) -> &RGBErrorKind {
79        &self.kind
80    }
81}
82
83impl Display for ParseRGBError {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match &self.kind {
86            RGBErrorKind::Size => {
87                "cannot parse rgb from string with size not equal to 6 (or 7 if '#' is used)".fmt(f)
88            }
89            RGBErrorKind::Invalid => "invalid string which contains UTF8 symbols".fmt(f),
90            RGBErrorKind::Format { pos, error } => {
91                error.fmt(f)?;
92                " on position ".fmt(f)?;
93                pos.fmt(f)
94            }
95        }
96    }
97}
98
99// rgb!(#234312)
100// rgb!(0xFF, 0xAA, 0xCC)