Skip to main content

tinted_builder/scheme/
base16.rs

1use serde::ser::{SerializeMap, SerializeStruct};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::{collections::HashMap, fmt};
4
5pub use crate::scheme::color::Color;
6
7use crate::{utils::slugify, SchemeSystem, SchemeVariant};
8
9pub const REQUIRED_BASE16_PALETTE_KEYS: [&str; 16] = [
10    "base00", "base01", "base02", "base03", "base04", "base05", "base06", "base07", "base08",
11    "base09", "base0A", "base0B", "base0C", "base0D", "base0E", "base0F",
12];
13
14pub const REQUIRED_BASE24_PALETTE_KEYS: [&str; 24] = [
15    "base00", "base01", "base02", "base03", "base04", "base05", "base06", "base07", "base08",
16    "base09", "base0A", "base0B", "base0C", "base0D", "base0E", "base0F", "base10", "base11",
17    "base12", "base13", "base14", "base15", "base16", "base17",
18];
19
20#[derive(Deserialize, Serialize)]
21struct SchemeWrapper {
22    pub(crate) system: SchemeSystem,
23    pub(crate) name: String,
24    pub(crate) slug: Option<String>,
25    pub(crate) author: String,
26    pub(crate) description: Option<String>,
27    pub(crate) variant: Option<SchemeVariant>,
28    pub(crate) palette: HashMap<String, String>,
29}
30
31#[derive(Debug, Clone)]
32pub struct Base16Scheme {
33    pub system: SchemeSystem,
34    pub name: String,
35    pub slug: String,
36    pub author: String,
37    pub description: Option<String>,
38    pub variant: SchemeVariant,
39    pub palette: HashMap<String, Color>,
40}
41
42impl fmt::Display for Base16Scheme {
43    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44        writeln!(f, "author: \"{}\"", self.author)?;
45        if let Some(ref desc) = self.description {
46            writeln!(f, "description: \"{desc}\"")?;
47        }
48        writeln!(f, "name: \"{}\"", self.name)?;
49        writeln!(f, "slug: \"{}\"", self.slug)?;
50        writeln!(f, "system: \"{}\"", self.system)?;
51        writeln!(f, "variant: \"{}\"", self.variant)?;
52        writeln!(f, "palette:")?;
53
54        let mut palette_vec: Vec<(String, Color)> = self
55            .palette
56            .clone()
57            .iter()
58            .map(|(k, v)| (k.clone(), v.clone()))
59            .collect();
60        palette_vec.sort_by_key(|k| k.0.clone());
61
62        for (key, value) in palette_vec {
63            writeln!(f, "  {key}: \"{value}\"")?;
64        }
65        Ok(())
66    }
67}
68
69impl<'de> Deserialize<'de> for Base16Scheme {
70    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
71    where
72        D: Deserializer<'de>,
73    {
74        let wrapper = SchemeWrapper::deserialize(deserializer)?;
75        let slug = wrapper
76            .slug
77            .map_or_else(|| slugify(&wrapper.name), |slug| slugify(&slug));
78        let variant = wrapper.variant.unwrap_or(SchemeVariant::Dark);
79
80        match wrapper.system {
81            SchemeSystem::Base16 => {
82                let contains_all_keys = REQUIRED_BASE16_PALETTE_KEYS
83                    .iter()
84                    .all(|&key| wrapper.palette.contains_key(key));
85
86                if !contains_all_keys {
87                    return Err(serde::de::Error::custom(format!(
88                        "{} scheme does not contain the required palette properties",
89                        wrapper.system
90                    )));
91                }
92            }
93            SchemeSystem::Base24 => {
94                let contains_all_keys = REQUIRED_BASE24_PALETTE_KEYS
95                    .iter()
96                    .all(|&key| wrapper.palette.contains_key(key));
97
98                if !contains_all_keys {
99                    return Err(serde::de::Error::custom(format!(
100                        "{} scheme does not contain the required palette properties",
101                        wrapper.system
102                    )));
103                }
104            }
105            SchemeSystem::List | SchemeSystem::ListBase16 | SchemeSystem::ListBase24 => {
106                return Err(serde::de::Error::custom(format!(
107                    "{} is not a valid Scheme system for a specific scheme",
108                    wrapper.system
109                )));
110            }
111        }
112
113        let palette_result: Result<HashMap<String, Color>, _> = wrapper
114            .palette
115            .into_iter()
116            .map(|(key, value)| {
117                Color::new(&value)
118                    .map(|color| (key, color))
119                    .map_err(|e| serde::de::Error::custom(e.to_string()))
120            })
121            .collect();
122
123        Ok(Self {
124            name: wrapper.name,
125            slug,
126            system: wrapper.system,
127            author: wrapper.author,
128            description: wrapper.description,
129            variant,
130            palette: palette_result?,
131        })
132    }
133}
134
135impl Serialize for Base16Scheme {
136    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137    where
138        S: Serializer,
139    {
140        let mut state = serializer.serialize_struct("Scheme", 7)?;
141        state.serialize_field("system", &self.system)?;
142        state.serialize_field("name", &self.name)?;
143        state.serialize_field("slug", &self.slug)?;
144        state.serialize_field("author", &self.author)?;
145        if let Some(description) = &self.description {
146            state.serialize_field("description", description)?;
147        }
148        state.serialize_field("variant", &self.variant)?;
149
150        // Collect and sort the palette by key
151        let mut sorted_palette: Vec<(&String, &Color)> = self.palette.iter().collect();
152        sorted_palette.sort_by(|a, b| a.0.cmp(b.0));
153
154        // Serialize the sorted palette as a map within the struct
155        state.serialize_field("palette", &SortedPalette(sorted_palette))?;
156
157        state.end()
158    }
159}
160
161// Helper struct for serializing sorted palette
162struct SortedPalette<'a>(Vec<(&'a String, &'a Color)>);
163
164#[allow(clippy::elidable_lifetime_names)]
165impl<'a> Serialize for SortedPalette<'a> {
166    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
167    where
168        S: Serializer,
169    {
170        let mut map = serializer.serialize_map(Some(self.0.len()))?;
171        for (key, value) in &self.0 {
172            map.serialize_entry(key, format!("#{}", &value.to_hex()).as_str())?;
173        }
174        map.end()
175    }
176}