1use crate::{SharedString, SharedUri, WindowContext};
2use collections::FxHashMap;
3use futures::Future;
4use parking_lot::Mutex;
5use std::any::TypeId;
6use std::hash::{Hash, Hasher};
7use std::sync::Arc;
8use std::{any::Any, path::PathBuf};
9
10#[derive(Debug, PartialEq, Eq, Hash, Clone)]
11pub(crate) enum UriOrPath {
12 Uri(SharedUri),
13 Path(Arc<PathBuf>),
14 Asset(SharedString),
15}
16
17impl From<SharedUri> for UriOrPath {
18 fn from(value: SharedUri) -> Self {
19 Self::Uri(value)
20 }
21}
22
23impl From<Arc<PathBuf>> for UriOrPath {
24 fn from(value: Arc<PathBuf>) -> Self {
25 Self::Path(value)
26 }
27}
28
29/// A trait for asynchronous asset loading.
30pub trait Asset {
31 /// The source of the asset.
32 type Source: Clone + Hash + Send;
33
34 /// The loaded asset
35 type Output: Clone + Send;
36
37 /// Load the asset asynchronously
38 fn load(
39 source: Self::Source,
40 cx: &mut WindowContext,
41 ) -> impl Future<Output = Self::Output> + Send + 'static;
42}
43
44/// Use a quick, non-cryptographically secure hash function to get an identifier from data
45pub fn hash<T: Hash>(data: &T) -> u64 {
46 let mut hasher = collections::FxHasher::default();
47 data.hash(&mut hasher);
48 hasher.finish()
49}
50
51/// A cache for assets.
52#[derive(Clone)]
53pub struct AssetCache {
54 assets: Arc<Mutex<FxHashMap<(TypeId, u64), Box<dyn Any + Send>>>>,
55}
56
57impl AssetCache {
58 pub(crate) fn new() -> Self {
59 Self {
60 assets: Default::default(),
61 }
62 }
63
64 /// Get the asset from the cache, if it exists.
65 pub fn get<A: Asset + 'static>(&self, source: &A::Source) -> Option<A::Output> {
66 self.assets
67 .lock()
68 .get(&(TypeId::of::<A>(), hash(&source)))
69 .and_then(|task| task.downcast_ref::<A::Output>())
70 .cloned()
71 }
72
73 /// Insert the asset into the cache.
74 pub fn insert<A: Asset + 'static>(&mut self, source: A::Source, output: A::Output) {
75 self.assets
76 .lock()
77 .insert((TypeId::of::<A>(), hash(&source)), Box::new(output));
78 }
79
80 /// Remove an entry from the asset cache
81 pub fn remove<A: Asset + 'static>(&mut self, source: &A::Source) -> Option<A::Output> {
82 self.assets
83 .lock()
84 .remove(&(TypeId::of::<A>(), hash(&source)))
85 .and_then(|any| any.downcast::<A::Output>().ok())
86 .map(|boxed| *boxed)
87 }
88}