1#[allow(dead_code)]
4#[derive(Clone, PartialEq, Debug)]
5pub enum ResourceState {
6 Unloaded,
7 Loading,
8 Loaded,
9 Failed(String),
10}
11
12#[allow(dead_code)]
13pub struct Resource {
14 pub id: u32,
15 pub key: String,
16 pub resource_type: String,
17 pub state: ResourceState,
18 pub data: Option<Vec<u8>>,
19 pub ref_count: u32,
20 pub size_bytes: usize,
21}
22
23#[allow(dead_code)]
24pub struct ResourceManager {
25 pub resources: Vec<Resource>,
26 pub next_id: u32,
27 pub total_loaded_bytes: usize,
28}
29
30#[allow(dead_code)]
31pub fn new_resource_manager() -> ResourceManager {
32 ResourceManager {
33 resources: Vec::new(),
34 next_id: 1,
35 total_loaded_bytes: 0,
36 }
37}
38
39#[allow(dead_code)]
40pub fn register_resource(mgr: &mut ResourceManager, key: &str, rtype: &str) -> u32 {
41 let id = mgr.next_id;
42 mgr.next_id += 1;
43 mgr.resources.push(Resource {
44 id,
45 key: key.to_string(),
46 resource_type: rtype.to_string(),
47 state: ResourceState::Unloaded,
48 data: None,
49 ref_count: 0,
50 size_bytes: 0,
51 });
52 id
53}
54
55#[allow(dead_code)]
56pub fn load_resource(mgr: &mut ResourceManager, id: u32, data: Vec<u8>) {
57 if let Some(r) = mgr.resources.iter_mut().find(|r| r.id == id) {
58 let size = data.len();
59 if r.state == ResourceState::Loaded {
61 mgr.total_loaded_bytes = mgr.total_loaded_bytes.saturating_sub(r.size_bytes);
62 }
63 r.size_bytes = size;
64 r.data = Some(data);
65 r.state = ResourceState::Loaded;
66 mgr.total_loaded_bytes += size;
67 }
68}
69
70#[allow(dead_code)]
71pub fn fail_resource(mgr: &mut ResourceManager, id: u32, reason: &str) {
72 if let Some(r) = mgr.resources.iter_mut().find(|r| r.id == id) {
73 if r.state == ResourceState::Loaded {
74 mgr.total_loaded_bytes = mgr.total_loaded_bytes.saturating_sub(r.size_bytes);
75 }
76 r.state = ResourceState::Failed(reason.to_string());
77 r.data = None;
78 r.size_bytes = 0;
79 }
80}
81
82#[allow(dead_code)]
83pub fn unload_resource(mgr: &mut ResourceManager, id: u32) {
84 if let Some(r) = mgr.resources.iter_mut().find(|r| r.id == id) {
85 if r.state == ResourceState::Loaded {
86 mgr.total_loaded_bytes = mgr.total_loaded_bytes.saturating_sub(r.size_bytes);
87 }
88 r.data = None;
89 r.size_bytes = 0;
90 r.state = ResourceState::Unloaded;
91 }
92}
93
94#[allow(dead_code)]
95pub fn get_resource(mgr: &ResourceManager, id: u32) -> Option<&Resource> {
96 mgr.resources.iter().find(|r| r.id == id)
97}
98
99#[allow(dead_code)]
100pub fn get_by_key<'a>(mgr: &'a ResourceManager, key: &str) -> Option<&'a Resource> {
101 mgr.resources.iter().find(|r| r.key == key)
102}
103
104#[allow(dead_code)]
105pub fn retain_resource(mgr: &mut ResourceManager, id: u32) {
106 if let Some(r) = mgr.resources.iter_mut().find(|r| r.id == id) {
107 r.ref_count += 1;
108 }
109}
110
111#[allow(dead_code)]
112pub fn release_resource(mgr: &mut ResourceManager, id: u32) {
113 let should_unload = if let Some(r) = mgr.resources.iter_mut().find(|r| r.id == id) {
114 if r.ref_count > 0 {
115 r.ref_count -= 1;
116 }
117 r.ref_count == 0 && r.state == ResourceState::Loaded
118 } else {
119 false
120 };
121 if should_unload {
122 unload_resource(mgr, id);
123 }
124}
125
126#[allow(dead_code)]
127pub fn loaded_count(mgr: &ResourceManager) -> usize {
128 mgr.resources
129 .iter()
130 .filter(|r| r.state == ResourceState::Loaded)
131 .count()
132}
133
134#[allow(dead_code)]
135pub fn failed_count(mgr: &ResourceManager) -> usize {
136 mgr.resources
137 .iter()
138 .filter(|r| matches!(r.state, ResourceState::Failed(_)))
139 .count()
140}
141
142#[allow(dead_code)]
143pub fn total_memory(mgr: &ResourceManager) -> usize {
144 mgr.total_loaded_bytes
145}
146
147#[allow(dead_code)]
148pub fn garbage_collect(mgr: &mut ResourceManager) {
149 let ids_to_unload: Vec<u32> = mgr
150 .resources
151 .iter()
152 .filter(|r| r.ref_count == 0 && r.state == ResourceState::Loaded)
153 .map(|r| r.id)
154 .collect();
155 for id in ids_to_unload {
156 unload_resource(mgr, id);
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn test_new_resource_manager() {
166 let mgr = new_resource_manager();
167 assert!(mgr.resources.is_empty());
168 assert_eq!(mgr.total_loaded_bytes, 0);
169 }
170
171 #[test]
172 fn test_register_resource() {
173 let mut mgr = new_resource_manager();
174 let id = register_resource(&mut mgr, "texture/grass", "texture");
175 assert_eq!(id, 1);
176 let r = get_resource(&mgr, id).expect("should succeed");
177 assert_eq!(r.state, ResourceState::Unloaded);
178 assert_eq!(r.key, "texture/grass");
179 }
180
181 #[test]
182 fn test_load_resource() {
183 let mut mgr = new_resource_manager();
184 let id = register_resource(&mut mgr, "mesh/head", "mesh");
185 load_resource(&mut mgr, id, vec![1u8; 1024]);
186 let r = get_resource(&mgr, id).expect("should succeed");
187 assert_eq!(r.state, ResourceState::Loaded);
188 assert_eq!(r.size_bytes, 1024);
189 assert_eq!(total_memory(&mgr), 1024);
190 }
191
192 #[test]
193 fn test_unload_resource() {
194 let mut mgr = new_resource_manager();
195 let id = register_resource(&mut mgr, "a", "t");
196 load_resource(&mut mgr, id, vec![0u8; 512]);
197 unload_resource(&mut mgr, id);
198 let r = get_resource(&mgr, id).expect("should succeed");
199 assert_eq!(r.state, ResourceState::Unloaded);
200 assert_eq!(total_memory(&mgr), 0);
201 }
202
203 #[test]
204 fn test_fail_resource() {
205 let mut mgr = new_resource_manager();
206 let id = register_resource(&mut mgr, "b", "t");
207 fail_resource(&mut mgr, id, "file not found");
208 let r = get_resource(&mgr, id).expect("should succeed");
209 assert!(matches!(r.state, ResourceState::Failed(_)));
210 }
211
212 #[test]
213 fn test_retain_release_auto_unload() {
214 let mut mgr = new_resource_manager();
215 let id = register_resource(&mut mgr, "c", "t");
216 load_resource(&mut mgr, id, vec![1u8; 256]);
217 retain_resource(&mut mgr, id);
218 assert_eq!(get_resource(&mgr, id).expect("should succeed").ref_count, 1);
219 release_resource(&mut mgr, id);
220 let r = get_resource(&mgr, id).expect("should succeed");
221 assert_eq!(r.state, ResourceState::Unloaded);
222 assert_eq!(total_memory(&mgr), 0);
223 }
224
225 #[test]
226 fn test_total_memory_multiple() {
227 let mut mgr = new_resource_manager();
228 let id1 = register_resource(&mut mgr, "r1", "t");
229 let id2 = register_resource(&mut mgr, "r2", "t");
230 load_resource(&mut mgr, id1, vec![0u8; 100]);
231 load_resource(&mut mgr, id2, vec![0u8; 200]);
232 assert_eq!(total_memory(&mgr), 300);
233 }
234
235 #[test]
236 fn test_garbage_collect() {
237 let mut mgr = new_resource_manager();
238 let id1 = register_resource(&mut mgr, "g1", "t");
239 let id2 = register_resource(&mut mgr, "g2", "t");
240 load_resource(&mut mgr, id1, vec![0u8; 50]);
241 load_resource(&mut mgr, id2, vec![0u8; 50]);
242 retain_resource(&mut mgr, id1);
243 garbage_collect(&mut mgr);
245 assert_eq!(
246 get_resource(&mgr, id2).expect("should succeed").state,
247 ResourceState::Unloaded
248 );
249 assert_eq!(
250 get_resource(&mgr, id1).expect("should succeed").state,
251 ResourceState::Loaded
252 );
253 }
254
255 #[test]
256 fn test_get_by_key() {
257 let mut mgr = new_resource_manager();
258 let id = register_resource(&mut mgr, "unique/key", "mesh");
259 load_resource(&mut mgr, id, vec![1u8; 32]);
260 let r = get_by_key(&mgr, "unique/key").expect("should succeed");
261 assert_eq!(r.id, id);
262 }
263
264 #[test]
265 fn test_loaded_count() {
266 let mut mgr = new_resource_manager();
267 let id1 = register_resource(&mut mgr, "k1", "t");
268 let id2 = register_resource(&mut mgr, "k2", "t");
269 register_resource(&mut mgr, "k3", "t");
270 load_resource(&mut mgr, id1, vec![0u8; 10]);
271 load_resource(&mut mgr, id2, vec![0u8; 10]);
272 assert_eq!(loaded_count(&mgr), 2);
273 }
274
275 #[test]
276 fn test_failed_count() {
277 let mut mgr = new_resource_manager();
278 let id1 = register_resource(&mut mgr, "f1", "t");
279 let id2 = register_resource(&mut mgr, "f2", "t");
280 register_resource(&mut mgr, "f3", "t");
281 fail_resource(&mut mgr, id1, "err");
282 fail_resource(&mut mgr, id2, "err2");
283 assert_eq!(failed_count(&mgr), 2);
284 }
285
286 #[test]
287 fn test_ids_increment() {
288 let mut mgr = new_resource_manager();
289 let id1 = register_resource(&mut mgr, "a", "t");
290 let id2 = register_resource(&mut mgr, "b", "t");
291 assert!(id2 > id1);
292 }
293
294 #[test]
295 fn test_retain_multiple_refs() {
296 let mut mgr = new_resource_manager();
297 let id = register_resource(&mut mgr, "multi", "t");
298 load_resource(&mut mgr, id, vec![0u8; 100]);
299 retain_resource(&mut mgr, id);
300 retain_resource(&mut mgr, id);
301 release_resource(&mut mgr, id);
302 assert_eq!(
304 get_resource(&mgr, id).expect("should succeed").state,
305 ResourceState::Loaded
306 );
307 release_resource(&mut mgr, id);
308 assert_eq!(
310 get_resource(&mgr, id).expect("should succeed").state,
311 ResourceState::Unloaded
312 );
313 }
314}