Add `AnyAssetSource`

Marshall Bowers created

Change summary

crates/gpui2/src/app.rs             | 12 ++++++++++--
crates/gpui2/src/assets.rs          | 24 +++++++++++++++++++-----
crates/gpui2/src/svg_renderer.rs    |  9 ++++-----
crates/storybook2/src/assets.rs     |  4 ++--
crates/storybook2/src/storybook2.rs |  9 ++++-----
crates/zed2/src/main.rs             | 17 +++++++++--------
6 files changed, 48 insertions(+), 27 deletions(-)

Detailed changes

crates/gpui2/src/app.rs 🔗

@@ -13,7 +13,7 @@ use crate::{
     ClipboardItem, Context, DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId,
     KeyBinding, Keymap, LayoutId, MainThread, MainThreadOnly, Pixels, Platform, Point,
     SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement,
-    TextSystem, View, Window, WindowContext, WindowHandle, WindowId,
+    TextSystem, View, Window, WindowContext, WindowHandle, WindowId, AnyAssetSource,
 };
 use anyhow::{anyhow, Result};
 use collections::{HashMap, HashSet, VecDeque};
@@ -66,6 +66,8 @@ impl App {
             os_version: platform.os_version().ok(),
             app_version: platform.app_version().ok(),
         };
+        let asset_source = AnyAssetSource(asset_source);
+
         Self(Arc::new_cyclic(|this| {
             Mutex::new(AppContext {
                 this: this.clone(),
@@ -76,7 +78,8 @@ impl App {
                 pending_updates: 0,
                 next_frame_callbacks: Default::default(),
                 executor,
-                svg_renderer: SvgRenderer::new(asset_source),
+                svg_renderer: SvgRenderer::new(asset_source.clone()),
+                asset_source,
                 image_cache: ImageCache::new(http_client),
                 text_style_stack: Vec::new(),
                 globals_by_type: HashMap::default(),
@@ -179,6 +182,7 @@ pub struct AppContext {
     pub(crate) next_frame_callbacks: HashMap<DisplayId, Vec<FrameCallback>>,
     pub(crate) executor: Executor,
     pub(crate) svg_renderer: SvgRenderer,
+    asset_source: AnyAssetSource,
     pub(crate) image_cache: ImageCache,
     pub(crate) text_style_stack: Vec<TextStyleRefinement>,
     pub(crate) globals_by_type: HashMap<TypeId, AnyBox>,
@@ -506,6 +510,10 @@ impl AppContext {
         });
     }
 
+    pub fn asset_source(&self) -> &AnyAssetSource {
+        &self.asset_source
+    }
+
     pub fn text_system(&self) -> &Arc<TextSystem> {
         &self.text_system
     }

crates/gpui2/src/assets.rs 🔗

@@ -5,23 +5,37 @@ use std::{
     borrow::Cow,
     fmt,
     hash::Hash,
-    sync::atomic::{AtomicUsize, Ordering::SeqCst},
+    sync::{atomic::{AtomicUsize, Ordering::SeqCst}, Arc},
 };
 
 pub trait AssetSource: 'static + Send + Sync {
-    fn load(&self, path: &SharedString) -> Result<Cow<[u8]>>;
-    fn list(&self, path: &SharedString) -> Result<Vec<SharedString>>;
+    fn load(&self, path: SharedString) -> Result<Cow<[u8]>>;
+    fn list(&self, path: SharedString) -> Result<Vec<SharedString>>;
 }
 
+#[derive(Clone)]
+pub struct AnyAssetSource(pub(crate) Arc<dyn AssetSource>);
+
+impl AnyAssetSource {
+    pub fn load(&self, path: impl Into<SharedString>) -> Result<Cow<[u8]>> {
+        self.0.load(path.into())
+    }
+
+    pub fn list(&self, path: impl Into<SharedString>) -> Result<Vec<SharedString>> {
+        self.0.list(path.into())
+    }
+}
+
+
 impl AssetSource for () {
-    fn load(&self, path: &SharedString) -> Result<Cow<[u8]>> {
+    fn load(&self, path: SharedString) -> Result<Cow<[u8]>> {
         Err(anyhow!(
             "get called on empty asset provider with \"{}\"",
             path
         ))
     }
 
-    fn list(&self, _path: &SharedString) -> Result<Vec<SharedString>> {
+    fn list(&self, _path: SharedString) -> Result<Vec<SharedString>> {
         Ok(vec![])
     }
 }

crates/gpui2/src/svg_renderer.rs 🔗

@@ -1,7 +1,6 @@
-use crate::{AssetSource, DevicePixels, IsZero, Result, SharedString, Size};
+use crate::{DevicePixels, IsZero, Result, SharedString, Size, AnyAssetSource};
 use anyhow::anyhow;
 use std::hash::Hash;
-use std::sync::Arc;
 
 #[derive(Clone, PartialEq, Hash, Eq)]
 pub struct RenderSvgParams {
@@ -10,11 +9,11 @@ pub struct RenderSvgParams {
 }
 
 pub struct SvgRenderer {
-    asset_source: Arc<dyn AssetSource>,
+    asset_source: AnyAssetSource,
 }
 
 impl SvgRenderer {
-    pub fn new(asset_source: Arc<dyn AssetSource>) -> Self {
+    pub fn new(asset_source: AnyAssetSource) -> Self {
         Self { asset_source }
     }
 
@@ -24,7 +23,7 @@ impl SvgRenderer {
         }
 
         // Load the tree.
-        let bytes = self.asset_source.load(&params.path)?;
+        let bytes = self.asset_source.load(params.path.clone())?;
         let tree = usvg::Tree::from_data(&bytes, &usvg::Options::default())?;
 
         // Render the SVG to a pixmap with the specified width and height.

crates/storybook2/src/assets.rs 🔗

@@ -15,13 +15,13 @@ use rust_embed::RustEmbed;
 pub struct Assets;
 
 impl AssetSource for Assets {
-    fn load(&self, path: &SharedString) -> Result<Cow<[u8]>> {
+    fn load(&self, path: SharedString) -> Result<Cow<[u8]>> {
         Self::get(path.as_ref())
             .map(|f| f.data)
             .ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
     }
 
-    fn list(&self, path: &SharedString) -> Result<Vec<SharedString>> {
+    fn list(&self, path: SharedString) -> Result<Vec<SharedString>> {
         Ok(Self::iter()
             .filter(|p| p.starts_with(path.as_ref()))
             .map(SharedString::from)

crates/storybook2/src/storybook2.rs 🔗

@@ -10,7 +10,7 @@ use std::sync::Arc;
 
 use clap::Parser;
 use gpui2::{
-    div, px, size, view, AnyView, AppContext, AssetSource, Bounds, Context, Element, ViewContext,
+    div, px, size, view, AnyView, AppContext, Bounds, Context, Element, ViewContext,
     WindowBounds, WindowOptions,
 };
 use log::LevelFilter;
@@ -104,12 +104,11 @@ impl StoryWrapper {
 }
 
 fn load_embedded_fonts(cx: &AppContext) -> gpui2::Result<()> {
-    let font_paths = Assets.list(&"fonts".into())?;
+    let font_paths = cx.asset_source().list("fonts")?;
     let mut embedded_fonts = Vec::new();
-    for font_path in &font_paths {
+    for font_path in font_paths {
         if font_path.ends_with(".ttf") {
-            let font_path = &*font_path;
-            let font_bytes = Assets.load(font_path)?.to_vec();
+            let font_bytes = cx.asset_source().load(font_path)?.to_vec();
             embedded_fonts.push(Arc::from(font_bytes));
         }
     }

crates/zed2/src/main.rs 🔗

@@ -67,8 +67,6 @@ fn main() {
     let session_id = Uuid::new_v4().to_string();
     init_panic_hook(&app, installation_id.clone(), session_id.clone());
 
-    load_embedded_fonts(&app);
-
     let fs = Arc::new(RealFs);
     let user_settings_file_rx =
         watch_config_file(&app.executor(), fs.clone(), paths::SETTINGS.clone());
@@ -101,6 +99,7 @@ fn main() {
 
     app.run(move |cx| {
         cx.set_global(*RELEASE_CHANNEL);
+        load_embedded_fonts(cx);
 
         let mut store = SettingsStore::default();
         store
@@ -635,10 +634,12 @@ fn collect_url_args() -> Vec<String> {
         .collect()
 }
 
-fn load_embedded_fonts(app: &App) {
-    let font_paths = Assets.list(&"fonts".into()).unwrap();
+fn load_embedded_fonts(cx: &AppContext) {
+    let asset_source = cx.asset_source();
+    let font_paths = asset_source.list("fonts").unwrap();
     let embedded_fonts = Mutex::new(Vec::new());
-    let executor = app.executor();
+    let executor = cx.executor();
+
     executor.block(executor.scoped(|scope| {
         for font_path in &font_paths {
             if !font_path.ends_with(".ttf") {
@@ -646,13 +647,13 @@ fn load_embedded_fonts(app: &App) {
             }
 
             scope.spawn(async {
-                let font_path = &*font_path;
-                let font_bytes = Assets.load(font_path).unwrap().to_vec();
+                let font_bytes = asset_source.load(font_path).unwrap().to_vec();
                 embedded_fonts.lock().push(Arc::from(font_bytes));
             });
         }
     }));
-    app.text_system()
+
+    cx.text_system()
         .add_fonts(&embedded_fonts.into_inner())
         .unwrap();
 }