unity_asset_binary/bundle/
loader.rs1use super::parser::BundleParser;
7use super::types::{AssetBundle, BundleLoadOptions};
8use crate::asset::Asset;
9use crate::error::{BinaryError, Result};
10use std::collections::HashMap;
11use std::path::Path;
12
13#[cfg(feature = "async")]
14use tokio::fs;
15
16pub struct BundleLoader {
21 bundles: HashMap<String, AssetBundle>,
23 options: BundleLoadOptions,
25}
26
27impl BundleLoader {
28 pub fn new() -> Self {
30 Self {
31 bundles: HashMap::new(),
32 options: BundleLoadOptions::default(),
33 }
34 }
35
36 pub fn with_options(options: BundleLoadOptions) -> Self {
38 Self {
39 bundles: HashMap::new(),
40 options,
41 }
42 }
43
44 pub fn load_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<&AssetBundle> {
46 let path_ref = path.as_ref();
47 let path_str = path_ref.to_string_lossy().to_string();
48
49 if self.bundles.contains_key(&path_str) {
51 return Ok(self.bundles.get(&path_str).unwrap());
52 }
53
54 let data = std::fs::read(path_ref)
56 .map_err(|e| BinaryError::generic(format!("Failed to read bundle file: {}", e)))?;
57
58 let bundle = BundleParser::from_bytes_with_options(data, self.options.clone())?;
60
61 self.bundles.insert(path_str.clone(), bundle);
63 Ok(self.bundles.get(&path_str).unwrap())
64 }
65
66 pub fn load_from_memory(&mut self, name: String, data: Vec<u8>) -> Result<&AssetBundle> {
68 if self.bundles.contains_key(&name) {
70 return Ok(self.bundles.get(&name).unwrap());
71 }
72
73 let bundle = BundleParser::from_bytes_with_options(data, self.options.clone())?;
75
76 self.bundles.insert(name.clone(), bundle);
78 Ok(self.bundles.get(&name).unwrap())
79 }
80
81 #[cfg(feature = "async")]
83 pub async fn load_from_file_async<P: AsRef<Path>>(&mut self, path: P) -> Result<&AssetBundle> {
84 let path_ref = path.as_ref();
85 let path_str = path_ref.to_string_lossy().to_string();
86
87 if self.bundles.contains_key(&path_str) {
89 return Ok(self.bundles.get(&path_str).unwrap());
90 }
91
92 let data = fs::read(path_ref)
94 .await
95 .map_err(|e| BinaryError::generic(format!("Failed to read bundle file: {}", e)))?;
96
97 let bundle = BundleParser::from_bytes_with_options(data, self.options.clone())?;
99
100 self.bundles.insert(path_str.clone(), bundle);
102 Ok(self.bundles.get(&path_str).unwrap())
103 }
104
105 pub fn get_bundle(&self, name: &str) -> Option<&AssetBundle> {
107 self.bundles.get(name)
108 }
109
110 pub fn get_bundle_mut(&mut self, name: &str) -> Option<&mut AssetBundle> {
112 self.bundles.get_mut(name)
113 }
114
115 pub fn unload_bundle(&mut self, name: &str) -> bool {
117 self.bundles.remove(name).is_some()
118 }
119
120 pub fn unload_all(&mut self) {
122 self.bundles.clear();
123 }
124
125 pub fn loaded_bundles(&self) -> Vec<&str> {
127 self.bundles.keys().map(|s| s.as_str()).collect()
128 }
129
130 pub fn memory_usage(&self) -> usize {
132 self.bundles
133 .values()
134 .map(|bundle| bundle.size() as usize)
135 .sum()
136 }
137
138 pub fn find_assets_by_name(&self, name: &str) -> Vec<(&str, &Asset)> {
140 let mut results = Vec::new();
141
142 for (bundle_name, bundle) in &self.bundles {
143 for asset in &bundle.assets {
144 if bundle_name.contains(name) {
146 results.push((bundle_name.as_str(), asset));
147 }
148 }
149 }
150
151 results
152 }
153
154 pub fn find_assets_by_type(&self, _type_id: i32) -> Vec<(&str, &Asset)> {
156 let mut results = Vec::new();
157
158 for (bundle_name, bundle) in &self.bundles {
159 for asset in &bundle.assets {
160 results.push((bundle_name.as_str(), asset));
164 }
165 }
166
167 results
168 }
169
170 pub fn get_statistics(&self) -> LoaderStatistics {
172 let bundle_count = self.bundles.len();
173 let total_size = self.memory_usage();
174 let total_assets: usize = self.bundles.values().map(|b| b.asset_count()).sum();
175 let total_files: usize = self.bundles.values().map(|b| b.file_count()).sum();
176
177 LoaderStatistics {
178 bundle_count,
179 total_size,
180 total_assets,
181 total_files,
182 average_bundle_size: if bundle_count > 0 {
183 total_size / bundle_count
184 } else {
185 0
186 },
187 }
188 }
189
190 pub fn validate_all(&self) -> Result<()> {
192 for (name, bundle) in &self.bundles {
193 bundle.validate().map_err(|e| {
194 BinaryError::generic(format!("Bundle '{}' validation failed: {}", name, e))
195 })?;
196 }
197 Ok(())
198 }
199
200 pub fn set_options(&mut self, options: BundleLoadOptions) {
202 self.options = options;
203 }
204
205 pub fn options(&self) -> &BundleLoadOptions {
207 &self.options
208 }
209}
210
211impl Default for BundleLoader {
212 fn default() -> Self {
213 Self::new()
214 }
215}
216
217pub struct BundleResourceManager {
222 loader: BundleLoader,
223 dependencies: HashMap<String, Vec<String>>,
224 reference_counts: HashMap<String, usize>,
225}
226
227impl BundleResourceManager {
228 pub fn new() -> Self {
230 Self {
231 loader: BundleLoader::new(),
232 dependencies: HashMap::new(),
233 reference_counts: HashMap::new(),
234 }
235 }
236
237 pub fn load_bundle<P: AsRef<Path>>(
239 &mut self,
240 path: P,
241 dependencies: Vec<String>,
242 ) -> Result<()> {
243 let path_str = path.as_ref().to_string_lossy().to_string();
244
245 for dep in &dependencies {
247 if !self.loader.bundles.contains_key(dep) {
248 return Err(BinaryError::generic(format!(
249 "Dependency '{}' not loaded",
250 dep
251 )));
252 }
253 *self.reference_counts.entry(dep.clone()).or_insert(0) += 1;
255 }
256
257 self.loader.load_from_file(path)?;
259
260 self.dependencies.insert(path_str.clone(), dependencies);
262 self.reference_counts.insert(path_str, 1);
263
264 Ok(())
265 }
266
267 pub fn unload_bundle(&mut self, name: &str) -> Result<()> {
269 if !self.reference_counts.contains_key(name) {
271 return Err(BinaryError::generic(format!(
272 "Bundle '{}' not loaded",
273 name
274 )));
275 }
276
277 let ref_count = self.reference_counts.get_mut(name).unwrap();
279 *ref_count -= 1;
280
281 if *ref_count == 0 {
283 if let Some(deps) = self.dependencies.remove(name) {
285 for dep in deps {
286 self.unload_bundle(&dep)?;
287 }
288 }
289
290 self.loader.unload_bundle(name);
292 self.reference_counts.remove(name);
293 }
294
295 Ok(())
296 }
297
298 pub fn loader(&self) -> &BundleLoader {
300 &self.loader
301 }
302
303 pub fn loader_mut(&mut self) -> &mut BundleLoader {
305 &mut self.loader
306 }
307
308 pub fn get_dependencies(&self, name: &str) -> Option<&Vec<String>> {
310 self.dependencies.get(name)
311 }
312
313 pub fn get_reference_count(&self, name: &str) -> usize {
315 self.reference_counts.get(name).copied().unwrap_or(0)
316 }
317}
318
319impl Default for BundleResourceManager {
320 fn default() -> Self {
321 Self::new()
322 }
323}
324
325#[derive(Debug, Clone)]
327pub struct LoaderStatistics {
328 pub bundle_count: usize,
329 pub total_size: usize,
330 pub total_assets: usize,
331 pub total_files: usize,
332 pub average_bundle_size: usize,
333}
334
335pub fn load_bundle<P: AsRef<Path>>(path: P) -> Result<AssetBundle> {
338 let data = std::fs::read(path)
339 .map_err(|e| BinaryError::generic(format!("Failed to read bundle file: {}", e)))?;
340 BundleParser::from_bytes(data)
341}
342
343pub fn load_bundle_from_memory(data: Vec<u8>) -> Result<AssetBundle> {
345 BundleParser::from_bytes(data)
346}
347
348pub fn load_bundle_with_options<P: AsRef<Path>>(
350 path: P,
351 options: BundleLoadOptions,
352) -> Result<AssetBundle> {
353 let data = std::fs::read(path)
354 .map_err(|e| BinaryError::generic(format!("Failed to read bundle file: {}", e)))?;
355 BundleParser::from_bytes_with_options(data, options)
356}
357
358#[cfg(feature = "async")]
359pub async fn load_bundle_async<P: AsRef<Path>>(path: P) -> Result<AssetBundle> {
361 let data = fs::read(path)
362 .await
363 .map_err(|e| BinaryError::generic(format!("Failed to read bundle file: {}", e)))?;
364 BundleParser::from_bytes(data)
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 #[test]
372 fn test_loader_creation() {
373 let loader = BundleLoader::new();
374 assert_eq!(loader.loaded_bundles().len(), 0);
375 assert_eq!(loader.memory_usage(), 0);
376 }
377
378 #[test]
379 fn test_resource_manager_creation() {
380 let manager = BundleResourceManager::new();
381 assert_eq!(manager.loader().loaded_bundles().len(), 0);
382 }
383}