hypen_engine/lifecycle/
resource.rs1use indexmap::IndexMap;
2use std::sync::Arc;
3
4#[derive(Clone)]
6pub struct Resource {
7 pub data: Arc<Vec<u8>>,
9
10 pub mime_type: String,
12
13 pub fingerprint: Option<String>,
15}
16
17impl Resource {
18 pub fn new(data: Vec<u8>, mime_type: impl Into<String>) -> Self {
19 Self {
20 data: Arc::new(data),
21 mime_type: mime_type.into(),
22 fingerprint: None,
23 }
24 }
25
26 pub fn with_fingerprint(mut self, fingerprint: impl Into<String>) -> Self {
27 self.fingerprint = Some(fingerprint.into());
28 self
29 }
30}
31
32pub type ResourceFetcher = Box<dyn Fn(&str) -> Option<Resource> + Send + Sync>;
34
35pub struct ResourceCache {
37 cache: IndexMap<String, Resource>,
39
40 fetcher: Option<ResourceFetcher>,
42}
43
44impl ResourceCache {
45 pub fn new() -> Self {
46 Self {
47 cache: IndexMap::new(),
48 fetcher: None,
49 }
50 }
51
52 pub fn set_fetcher<F>(&mut self, fetcher: F)
54 where
55 F: Fn(&str) -> Option<Resource> + Send + Sync + 'static,
56 {
57 self.fetcher = Some(Box::new(fetcher));
58 }
59
60 pub fn get(&mut self, url: &str) -> Option<Resource> {
62 if let Some(resource) = self.cache.get(url) {
64 return Some(resource.clone());
65 }
66
67 if let Some(ref fetcher) = self.fetcher {
69 if let Some(resource) = fetcher(url) {
70 self.cache.insert(url.to_string(), resource.clone());
71 return Some(resource);
72 }
73 }
74
75 None
76 }
77
78 pub fn insert(&mut self, url: impl Into<String>, resource: Resource) {
80 self.cache.insert(url.into(), resource);
81 }
82
83 pub fn clear(&mut self) {
85 self.cache.clear();
86 }
87
88 pub fn remove(&mut self, url: &str) -> Option<Resource> {
90 self.cache.shift_remove(url)
91 }
92
93 pub fn size(&self) -> usize {
95 self.cache.len()
96 }
97}
98
99impl Default for ResourceCache {
100 fn default() -> Self {
101 Self::new()
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_cache_insert_get() {
111 let mut cache = ResourceCache::new();
112 let resource = Resource::new(vec![1, 2, 3], "image/png");
113
114 cache.insert("test.png", resource.clone());
115
116 let retrieved = cache.get("test.png").unwrap();
117 assert_eq!(*retrieved.data, vec![1, 2, 3]);
118 assert_eq!(retrieved.mime_type, "image/png");
119 }
120
121 #[test]
122 fn test_custom_fetcher() {
123 let mut cache = ResourceCache::new();
124
125 cache.set_fetcher(|url| {
126 if url == "auto.png" {
127 Some(Resource::new(vec![4, 5, 6], "image/png"))
128 } else {
129 None
130 }
131 });
132
133 let resource = cache.get("auto.png").unwrap();
134 assert_eq!(*resource.data, vec![4, 5, 6]);
135
136 assert_eq!(cache.size(), 1);
138 }
139}