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