theme_registry.rs

 1use crate::{Theme, ThemeMeta};
 2use anyhow::{Context, Result};
 3use gpui::{fonts, AssetSource, FontCache};
 4use parking_lot::Mutex;
 5use serde_json::Value;
 6use std::{collections::HashMap, sync::Arc};
 7
 8pub struct ThemeRegistry {
 9    assets: Box<dyn AssetSource>,
10    themes: Mutex<HashMap<String, Arc<Theme>>>,
11    theme_data: Mutex<HashMap<String, Arc<Value>>>,
12    font_cache: Arc<FontCache>,
13}
14
15impl ThemeRegistry {
16    pub fn new(source: impl AssetSource, font_cache: Arc<FontCache>) -> Arc<Self> {
17        Arc::new(Self {
18            assets: Box::new(source),
19            themes: Default::default(),
20            theme_data: Default::default(),
21            font_cache,
22        })
23    }
24
25    pub fn list(&self, internal: bool, experiments: bool) -> impl Iterator<Item = ThemeMeta> + '_ {
26        let mut dirs = self.assets.list("themes/");
27
28        if !internal {
29            dirs = dirs
30                .into_iter()
31                .filter(|path| !path.starts_with("themes/Internal"))
32                .collect()
33        }
34
35        if !experiments {
36            dirs = dirs
37                .into_iter()
38                .filter(|path| !path.starts_with("themes/Experiments"))
39                .collect()
40        }
41
42        dirs.into_iter().filter_map(|path| {
43            let filename = path.strip_prefix("themes/")?;
44            let theme_name = filename.strip_suffix(".json")?;
45            self.get(theme_name).ok().map(|theme| theme.meta.clone())
46        })
47    }
48
49    pub fn clear(&self) {
50        self.theme_data.lock().clear();
51        self.themes.lock().clear();
52    }
53
54    pub fn get(&self, name: &str) -> Result<Arc<Theme>> {
55        if let Some(theme) = self.themes.lock().get(name) {
56            return Ok(theme.clone());
57        }
58
59        let asset_path = format!("themes/{}.json", name);
60        let theme_json = self
61            .assets
62            .load(&asset_path)
63            .with_context(|| format!("failed to load theme file {}", asset_path))?;
64
65        let mut theme: Theme = fonts::with_font_cache(self.font_cache.clone(), || {
66            serde_path_to_error::deserialize(&mut serde_json::Deserializer::from_slice(&theme_json))
67        })?;
68
69        // Reset name to be the file path, so that we can use it to access the stored themes
70        theme.meta.name = name.into();
71        let theme = Arc::new(theme);
72        self.themes.lock().insert(name.to_string(), theme.clone());
73        Ok(theme)
74    }
75}