theme_registry.rs

 1use crate::Theme;
 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) -> impl Iterator<Item = String> {
26        self.assets.list("themes/").into_iter().filter_map(|path| {
27            let filename = path.strip_prefix("themes/")?;
28            let theme_name = filename.strip_suffix(".json")?;
29            Some(theme_name.to_string())
30        })
31    }
32
33    pub fn clear(&self) {
34        self.theme_data.lock().clear();
35        self.themes.lock().clear();
36    }
37
38    pub fn get(&self, name: &str) -> Result<Arc<Theme>> {
39        if let Some(theme) = self.themes.lock().get(name) {
40            return Ok(theme.clone());
41        }
42
43        let asset_path = format!("themes/{}.json", name);
44        let theme_json = self
45            .assets
46            .load(&asset_path)
47            .with_context(|| format!("failed to load theme file {}", asset_path))?;
48
49        let mut theme: Theme = fonts::with_font_cache(self.font_cache.clone(), || {
50            serde_path_to_error::deserialize(&mut serde_json::Deserializer::from_slice(&theme_json))
51        })?;
52
53        theme.name = name.into();
54        let theme = Arc::new(theme);
55        self.themes.lock().insert(name.to_string(), theme.clone());
56        Ok(theme)
57    }
58}