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, staff: bool) -> impl Iterator<Item = ThemeMeta> + '_ {
26        let mut dirs = self.assets.list("themes/");
27
28        if !staff {
29            dirs = dirs
30                .into_iter()
31                .filter(|path| !path.starts_with("themes/staff"))
32                .collect()
33        }
34
35        dirs.into_iter().filter_map(|path| {
36            let filename = path.strip_prefix("themes/")?;
37            let theme_name = filename.strip_suffix(".json")?;
38            self.get(theme_name).ok().map(|theme| theme.meta.clone())
39        })
40    }
41
42    pub fn clear(&self) {
43        self.theme_data.lock().clear();
44        self.themes.lock().clear();
45    }
46
47    pub fn get(&self, name: &str) -> Result<Arc<Theme>> {
48        if let Some(theme) = self.themes.lock().get(name) {
49            return Ok(theme.clone());
50        }
51
52        let asset_path = format!("themes/{}.json", name);
53        let theme_json = self
54            .assets
55            .load(&asset_path)
56            .with_context(|| format!("failed to load theme file {}", asset_path))?;
57
58        // Allocate into the heap directly, the Theme struct is too large to fit in the stack.
59        let mut theme: Arc<Theme> = fonts::with_font_cache(self.font_cache.clone(), || {
60            serde_path_to_error::deserialize(&mut serde_json::Deserializer::from_slice(&theme_json))
61        })?;
62
63        // Reset name to be the file path, so that we can use it to access the stored themes
64        Arc::get_mut(&mut theme).unwrap().meta.name = name.into();
65        self.themes.lock().insert(name.to_string(), theme.clone());
66        Ok(theme)
67    }
68}