1use std::collections::HashMap;
2
3use ron::Value;
4
5use crate::{
6 config::{ThemeConfig, RESOURCE_KEY},
7 Selector, Style,
8};
9
10#[derive(Debug, Clone, Default, PartialEq)]
11pub struct Theme {
12 styles: HashMap<String, Style>,
13}
14
15impl Theme {
16 pub fn from_config(theme: ThemeConfig) -> Self {
17 let mut styles = HashMap::new();
18
19 for style_key in theme.styles.keys() {
20 let mut properties = HashMap::new();
21 Theme::read_properties(style_key, &theme, &mut properties);
22
23 let mut states = HashMap::new();
24
25 let base_key = theme.styles.get(style_key).unwrap().base.clone();
26
27 if let Some(base) = theme.styles.get(&base_key) {
28 for state_key in base.states.keys() {
29 let mut state = HashMap::new();
30 Theme::read_states(&base_key, state_key, &theme, &mut state);
31 states.insert(state_key.clone(), state);
32 }
33 }
34
35 for state_key in theme.styles.get(style_key).unwrap().states.keys() {
36 let mut state = HashMap::new();
37 Theme::read_states(style_key, state_key, &theme, &mut state);
38 states.insert(state_key.clone(), state);
39 }
40
41 styles.insert(style_key.clone(), Style { properties, states });
42 }
43
44 Theme { styles }
45 }
46
47 pub fn style(&self, key: &str) -> Option<&Style> {
48 self.styles.get(key)
49 }
50
51 pub fn properties<'a>(&'a self, selector: &Selector) -> Option<&'a HashMap<String, Value>> {
52 if !selector.dirty() {
53 return None;
54 }
55
56 if let Some(style) = &selector.style {
57 if let Some(state) = &selector.state {
58 return self.styles.get(style)?.states.get(state);
59 }
60
61 return Some(&self.styles.get(style)?.properties);
62 }
63
64 None
65 }
66
67 fn read_properties(key: &str, theme: &ThemeConfig, properties: &mut HashMap<String, Value>) {
68 if key.is_empty() {
69 return;
70 }
71
72 if let Some(style) = theme.styles.get(key) {
73 Theme::read_properties(&style.base, theme, properties);
74
75 for (key, value) in &style.properties {
76 Theme::read_property(key, value, theme, properties);
77 }
78 }
79 }
80
81 fn read_states(
82 style_key: &str,
83 state_key: &str,
84 theme: &ThemeConfig,
85 states: &mut HashMap<String, Value>,
86 ) {
87 if style_key.is_empty() || state_key.is_empty() {
88 return;
89 }
90
91 if let Some(style) = theme.styles.get(style_key) {
92 for (key, value) in &style.properties {
93 Theme::read_property(key, value, theme, states);
94 }
95
96 if let Some(state) = style.states.get(state_key) {
97 for (key, value) in state {
98 Theme::read_property(key, value, theme, states);
99 }
100 }
101 }
102 }
103
104 fn read_property(
105 key: &str,
106 value: &Value,
107 theme: &ThemeConfig,
108 map: &mut HashMap<String, Value>,
109 ) {
110 if let Ok(value) = value.clone().into_rust::<String>() {
111 if value.starts_with(RESOURCE_KEY) {
112 if let Some(value) = theme.resources.get(&value.replace(RESOURCE_KEY, "")) {
113 map.insert(key.to_string(), value.clone());
114 }
115 } else {
116 map.insert(key.to_string(), Value::String(value));
117 }
118 } else {
119 map.insert(key.to_string(), value.clone());
120 }
121 }
122}