1use std::borrow::Cow;
18
19use crate::{Color, FamilyId, Weight};
20
21#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
25pub struct ElementId(pub u32);
26
27impl ElementId {
28 pub const INVALID: ElementId = ElementId(u32::MAX);
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Hash)]
40pub struct ElementName(Cow<'static, str>);
41
42impl ElementName {
43 pub const fn from_static(s: &'static str) -> Self {
45 ElementName(Cow::Borrowed(s))
46 }
47
48 pub fn as_str(&self) -> &str {
49 &self.0
50 }
51
52 pub fn parent(&self) -> Option<ElementName> {
55 self.0
56 .rsplit_once('.')
57 .map(|(head, _)| ElementName(Cow::Owned(head.to_string())))
58 }
59}
60
61impl From<&'static str> for ElementName {
62 fn from(s: &'static str) -> Self {
63 ElementName::from_static(s)
64 }
65}
66
67impl From<String> for ElementName {
68 fn from(s: String) -> Self {
69 ElementName(Cow::Owned(s))
70 }
71}
72
73impl std::fmt::Display for ElementName {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 f.write_str(&self.0)
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Hash)]
86pub enum ElementOwner {
87 Core,
88 Mode(Cow<'static, str>),
89 Plugin(Cow<'static, str>),
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Hash)]
97pub enum ColorRef {
98 Palette(crate::PaletteKey),
99 Literal(Color),
100 Default,
101}
102
103impl From<crate::PaletteKey> for ColorRef {
104 fn from(k: crate::PaletteKey) -> Self {
105 ColorRef::Palette(k)
106 }
107}
108
109impl From<&'static str> for ColorRef {
114 fn from(s: &'static str) -> Self {
115 ColorRef::Palette(crate::PaletteKey::from_static(s))
116 }
117}
118
119impl From<Color> for ColorRef {
120 fn from(c: Color) -> Self {
121 ColorRef::Literal(c)
122 }
123}
124
125#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
130pub struct ModifierSet {
131 pub bold: Option<bool>,
132 pub italic: Option<bool>,
133 pub underline: Option<bool>,
134 pub dim: Option<bool>,
135 pub reverse: Option<bool>,
136}
137
138#[derive(Debug, Default, Clone, PartialEq)]
143pub struct StyleSpec {
144 pub inherit: Option<ElementName>,
147 pub fg: Option<ColorRef>,
148 pub bg: Option<ColorRef>,
149 pub modifiers: ModifierSet,
150 pub scale: Option<f32>,
153 pub family: Option<FamilyId>,
154 pub weight: Option<Weight>,
155}
156
157impl StyleSpec {
158 pub fn new() -> Self {
161 Self::default()
162 }
163
164 pub fn inherit(mut self, name: impl Into<ElementName>) -> Self {
166 self.inherit = Some(name.into());
167 self
168 }
169
170 pub fn fg(mut self, fg: impl Into<ColorRef>) -> Self {
173 self.fg = Some(fg.into());
174 self
175 }
176
177 pub fn bg(mut self, bg: impl Into<ColorRef>) -> Self {
179 self.bg = Some(bg.into());
180 self
181 }
182
183 pub fn bold(mut self) -> Self {
184 self.modifiers.bold = Some(true);
185 self
186 }
187
188 pub fn italic(mut self) -> Self {
189 self.modifiers.italic = Some(true);
190 self
191 }
192
193 pub fn underline(mut self) -> Self {
194 self.modifiers.underline = Some(true);
195 self
196 }
197
198 pub fn dim(mut self) -> Self {
199 self.modifiers.dim = Some(true);
200 self
201 }
202
203 pub fn reverse(mut self) -> Self {
204 self.modifiers.reverse = Some(true);
205 self
206 }
207
208 pub fn no_bold(mut self) -> Self {
210 self.modifiers.bold = Some(false);
211 self
212 }
213
214 pub fn scale(mut self, ratio: f32) -> Self {
216 self.scale = Some(ratio);
217 self
218 }
219
220 pub fn family(mut self, family: FamilyId) -> Self {
221 self.family = Some(family);
222 self
223 }
224
225 pub fn weight(mut self, weight: Weight) -> Self {
226 self.weight = Some(weight);
227 self
228 }
229}
230
231#[derive(Debug, Clone)]
234pub struct ThemeElement {
235 pub id: ElementId,
236 pub name: ElementName,
237 pub owner: ElementOwner,
238 pub default: StyleSpec,
239 pub doc: &'static str,
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn element_name_parent_walks_dotted_segments() {
249 let n = ElementName::from_static("markdown.heading.1");
250 let p = n.parent().expect("has parent");
251 assert_eq!(p.as_str(), "markdown.heading");
252 let pp = p.parent().expect("has grandparent");
253 assert_eq!(pp.as_str(), "markdown");
254 assert_eq!(pp.parent(), None);
255 }
256
257 #[test]
258 fn color_ref_from_conversions() {
259 assert_eq!(
260 ColorRef::from(Color::Rgb(1, 2, 3)),
261 ColorRef::Literal(Color::Rgb(1, 2, 3))
262 );
263 }
264
265 #[test]
266 fn modifier_set_defaults_to_all_inherit() {
267 let m = ModifierSet::default();
268 assert_eq!(m.bold, None);
269 assert_eq!(m.reverse, None);
270 }
271
272 #[test]
273 fn style_spec_builder_sets_tri_state_modifiers() {
274 let s = StyleSpec::new().bold().no_bold();
275 assert_eq!(s.modifiers.bold, Some(false));
276 let s2 = StyleSpec::new().italic();
277 assert_eq!(s2.modifiers.italic, Some(true));
278 assert_eq!(s2.modifiers.bold, None);
279 }
280}