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