Skip to main content

ggsci/
palette.rs

1use crate::{Error, Rgb};
2
3/// The implementation kind for a palette.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum PaletteKind {
6    /// A static palette backed by checked-in color data.
7    Static,
8}
9
10/// A canonical static palette specification.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct PaletteSpec {
13    family: &'static str,
14    variant: &'static str,
15}
16
17impl PaletteSpec {
18    /// Creates a palette specification from canonical family and variant names.
19    #[must_use]
20    pub const fn new(family: &'static str, variant: &'static str) -> Self {
21        Self { family, variant }
22    }
23
24    /// Returns the canonical family name.
25    #[must_use]
26    pub const fn family(self) -> &'static str {
27        self.family
28    }
29
30    /// Returns the canonical variant name.
31    #[must_use]
32    pub const fn variant(self) -> &'static str {
33        self.variant
34    }
35}
36
37/// A generated ggsci color palette.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct Palette {
40    family: &'static str,
41    variant: &'static str,
42    kind: PaletteKind,
43    colors: &'static [Rgb],
44}
45
46impl Palette {
47    /// Creates a palette from canonical metadata and static colors.
48    #[must_use]
49    pub const fn new(
50        family: &'static str,
51        variant: &'static str,
52        kind: PaletteKind,
53        colors: &'static [Rgb],
54    ) -> Self {
55        Self {
56            family,
57            variant,
58            kind,
59            colors,
60        }
61    }
62
63    /// Returns the canonical family name.
64    #[must_use]
65    pub const fn family(&self) -> &'static str {
66        self.family
67    }
68
69    /// Returns the canonical variant name.
70    #[must_use]
71    pub const fn variant(&self) -> &'static str {
72        self.variant
73    }
74
75    /// Returns this palette's implementation kind.
76    #[must_use]
77    pub const fn kind(&self) -> PaletteKind {
78        self.kind
79    }
80
81    /// Returns this palette's canonical specification.
82    #[must_use]
83    pub const fn spec(&self) -> PaletteSpec {
84        PaletteSpec::new(self.family, self.variant)
85    }
86
87    /// Returns all colors in the palette.
88    #[must_use]
89    pub const fn colors(&self) -> &'static [Rgb] {
90        self.colors
91    }
92
93    /// Returns the number of colors in the palette.
94    #[must_use]
95    pub const fn len(&self) -> usize {
96        self.colors.len()
97    }
98
99    /// Returns `true` if the palette has no colors.
100    #[must_use]
101    pub const fn is_empty(&self) -> bool {
102        self.colors.is_empty()
103    }
104
105    /// Returns the first `n` colors.
106    ///
107    /// # Errors
108    ///
109    /// Returns [`Error::TooManyColorsRequested`] if `n` exceeds the palette
110    /// length. This method does not cycle colors.
111    pub fn take(&self, n: usize) -> Result<Vec<Rgb>, Error> {
112        if n > self.colors.len() {
113            return Err(Error::TooManyColorsRequested {
114                family: self.family,
115                variant: self.variant,
116                requested: n,
117                available: self.colors.len(),
118            });
119        }
120
121        Ok(self.colors[..n].to_vec())
122    }
123
124    /// Returns the first `n` colors as `#RRGGBB` strings.
125    ///
126    /// # Errors
127    ///
128    /// Returns [`Error::TooManyColorsRequested`] if `n` exceeds the palette
129    /// length. This method does not cycle colors.
130    pub fn take_hex(&self, n: usize) -> Result<Vec<String>, Error> {
131        self.take(n)
132            .map(|colors| colors.into_iter().map(Rgb::to_hex_string).collect())
133    }
134
135    /// Returns an explicit cycling iterator over the palette colors.
136    pub fn cycle(&self) -> impl Iterator<Item = Rgb> + '_ {
137        self.colors.iter().copied().cycle()
138    }
139}