use crate::{Theme, ThemeMeta}; use anyhow::{Context, Result}; use gpui::{fonts, AssetSource, FontCache}; use parking_lot::Mutex; use serde_json::Value; use std::{collections::HashMap, sync::Arc}; pub struct ThemeRegistry { assets: Box, themes: Mutex>>, theme_data: Mutex>>, font_cache: Arc, } impl ThemeRegistry { pub fn new(source: impl AssetSource, font_cache: Arc) -> Arc { Arc::new(Self { assets: Box::new(source), themes: Default::default(), theme_data: Default::default(), font_cache, }) } pub fn list(&self, staff: bool) -> impl Iterator + '_ { let mut dirs = self.assets.list("themes/"); if !staff { dirs = dirs .into_iter() .filter(|path| !path.starts_with("themes/staff")) .collect() } dirs.into_iter().filter_map(|path| { let filename = path.strip_prefix("themes/")?; let theme_name = filename.strip_suffix(".json")?; self.get(theme_name).ok().map(|theme| theme.meta.clone()) }) } pub fn clear(&self) { self.theme_data.lock().clear(); self.themes.lock().clear(); } pub fn get(&self, name: &str) -> Result> { if let Some(theme) = self.themes.lock().get(name) { return Ok(theme.clone()); } let asset_path = format!("themes/{}.json", name); let theme_json = self .assets .load(&asset_path) .with_context(|| format!("failed to load theme file {}", asset_path))?; // Allocate into the heap directly, the Theme struct is too large to fit in the stack. let mut theme: Arc = fonts::with_font_cache(self.font_cache.clone(), || { serde_path_to_error::deserialize(&mut serde_json::Deserializer::from_slice(&theme_json)) })?; // Reset name to be the file path, so that we can use it to access the stored themes Arc::get_mut(&mut theme).unwrap().meta.name = name.into(); self.themes.lock().insert(name.to_string(), theme.clone()); Ok(theme) } }