ori_core/style/
loader.rs

1use std::{
2    path::{Path, PathBuf},
3    time::SystemTime,
4};
5
6use crate::{StyleLoadError, Stylesheet};
7
8/// A style that has been loaded from a file.
9#[derive(Clone, Debug)]
10pub struct LoadedStyle {
11    modified: SystemTime,
12    path: PathBuf,
13    style: Stylesheet,
14}
15
16impl LoadedStyle {
17    /// Loads a style from a file.
18    pub fn load(path: impl AsRef<Path>) -> Result<Self, StyleLoadError> {
19        let modified = path.as_ref().metadata()?.modified()?;
20        let style = Stylesheet::load(&path)?;
21        Ok(Self {
22            modified,
23            path: path.as_ref().to_path_buf(),
24            style,
25        })
26    }
27
28    /// Reloads the style if the file has been modified.
29    ///
30    /// Returns true if the style was reloaded.
31    pub fn reload(&mut self) -> Result<bool, StyleLoadError> {
32        let modified = self.path.metadata()?.modified()?;
33
34        let needs_reload = modified > self.modified;
35        if needs_reload {
36            self.modified = modified;
37            self.style = Stylesheet::load(&self.path)?;
38        }
39
40        Ok(needs_reload)
41    }
42}
43
44/// A style that can be loaded from a file or be an inline style.
45#[derive(Clone, Debug)]
46pub enum LoadedStyleKind {
47    Loaded(LoadedStyle),
48    Inline(Stylesheet),
49}
50
51impl LoadedStyleKind {
52    /// Reloads the style if the file has been modified.
53    ///
54    /// Returns true if the style was reloaded.
55    pub fn reload(&mut self) -> Result<bool, StyleLoadError> {
56        match self {
57            Self::Loaded(loaded) => loaded.reload(),
58            Self::Inline(_) => Ok(false),
59        }
60    }
61
62    /// Returns the style.
63    pub fn style(&self) -> &Stylesheet {
64        match self {
65            Self::Loaded(style) => &style.style,
66            Self::Inline(style) => style,
67        }
68    }
69}
70
71impl From<Stylesheet> for LoadedStyleKind {
72    fn from(style: Stylesheet) -> Self {
73        Self::Inline(style)
74    }
75}
76
77impl TryFrom<&str> for LoadedStyleKind {
78    type Error = StyleLoadError;
79
80    fn try_from(path: &str) -> Result<Self, Self::Error> {
81        Ok(Self::Loaded(LoadedStyle::load(path)?))
82    }
83}
84
85impl TryFrom<&Path> for LoadedStyleKind {
86    type Error = StyleLoadError;
87
88    fn try_from(path: &Path) -> Result<Self, Self::Error> {
89        Ok(Self::Loaded(LoadedStyle::load(path)?))
90    }
91}
92
93/// A style loader that can load styles from files and inline styles.
94///
95/// Styles that are loaded from files are reloaded when the file is modified.
96#[derive(Clone, Debug)]
97pub struct StyleLoader {
98    styles: Vec<LoadedStyleKind>,
99    cache: Stylesheet,
100}
101
102impl StyleLoader {
103    /// Creates a new style loader.
104    pub fn new() -> Self {
105        Self {
106            styles: Vec::new(),
107            cache: Stylesheet::new(),
108        }
109    }
110
111    pub fn clear(&mut self) {
112        self.styles.clear();
113        self.cache = Stylesheet::new();
114    }
115
116    /// Adds a style to the loader.
117    pub fn add_style<T: TryInto<LoadedStyleKind>>(&mut self, style: T) -> Result<(), T::Error> {
118        self.styles.push(style.try_into()?);
119        self.compute_cache();
120        Ok(())
121    }
122
123    /// Recomputes the cache.
124    fn compute_cache(&mut self) {
125        self.cache = Stylesheet::new();
126
127        for style in self.styles.iter() {
128            self.cache.extend(style.style().clone());
129        }
130    }
131
132    /// Reloads the styles if the files have been modified.
133    pub fn reload(&mut self) -> Result<bool, StyleLoadError> {
134        let mut needs_reload = false;
135
136        for style in self.styles.iter_mut() {
137            if style.reload()? {
138                needs_reload = true;
139            }
140        }
141
142        if needs_reload {
143            self.compute_cache();
144        }
145
146        Ok(needs_reload)
147    }
148
149    /// Returns the style.
150    pub fn style(&self) -> &Stylesheet {
151        &self.cache
152    }
153}