1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::collections::HashMap;

use ron::Value;

use crate::{
    config::{ThemeConfig, RESOURCE_KEY},
    Selector, Style,
};

#[derive(Debug, Clone, Default, PartialEq)]
pub struct Theme {
    styles: HashMap<String, Style>,
}

impl Theme {
    pub fn from_config(theme: ThemeConfig) -> Self {
        let mut styles = HashMap::new();

        for style_key in theme.styles.keys() {
            let mut properties = HashMap::new();
            Theme::read_properties(style_key, &theme, &mut properties);

            let mut states = HashMap::new();

            let base_key = theme.styles.get(style_key).unwrap().base.clone();

            if let Some(base) = theme.styles.get(&base_key) {
                for state_key in base.states.keys() {
                    let mut state = HashMap::new();
                    Theme::read_states(&base_key, state_key, &theme, &mut state);
                    states.insert(state_key.clone(), state);
                }
            }

            for state_key in theme.styles.get(style_key).unwrap().states.keys() {
                let mut state = HashMap::new();
                Theme::read_states(style_key, state_key, &theme, &mut state);
                states.insert(state_key.clone(), state);
            }

            styles.insert(style_key.clone(), Style { properties, states });
        }

        Theme { styles }
    }

    pub fn style(&self, key: &str) -> Option<&Style> {
        self.styles.get(key)
    }

    pub fn properties<'a>(&'a self, selector: &Selector) -> Option<&'a HashMap<String, Value>> {
        if !selector.dirty() {
            return None;
        }

        if let Some(style) = &selector.style {
            if let Some(state) = &selector.state {
                return self.styles.get(style)?.states.get(state);
            }

            return Some(&self.styles.get(style)?.properties);
        }

        None
    }

    fn read_properties(key: &str, theme: &ThemeConfig, properties: &mut HashMap<String, Value>) {
        if key.is_empty() {
            return;
        }

        if let Some(style) = theme.styles.get(key) {
            Theme::read_properties(&style.base, theme, properties);

            for (key, value) in &style.properties {
                Theme::read_property(key, value, theme, properties);
            }
        }
    }

    fn read_states(
        style_key: &str,
        state_key: &str,
        theme: &ThemeConfig,
        states: &mut HashMap<String, Value>,
    ) {
        if style_key.is_empty() || state_key.is_empty() {
            return;
        }

        if let Some(style) = theme.styles.get(style_key) {
            for (key, value) in &style.properties {
                Theme::read_property(key, value, theme, states);
            }

            if let Some(state) = style.states.get(state_key) {
                for (key, value) in state {
                    Theme::read_property(key, value, theme, states);
                }
            }
        }
    }

    fn read_property(
        key: &str,
        value: &Value,
        theme: &ThemeConfig,
        map: &mut HashMap<String, Value>,
    ) {
        if let Ok(value) = value.clone().into_rust::<String>() {
            if value.starts_with(RESOURCE_KEY) {
                if let Some(value) = theme.resources.get(&value.replace(RESOURCE_KEY, "")) {
                    map.insert(key.to_string(), value.clone());
                }
            } else {
                map.insert(key.to_string(), Value::String(value));
            }
        } else {
            map.insert(key.to_string(), value.clone());
        }
    }
}