use super::parser::BundleParser;
use super::types::{AssetBundle, BundleLoadOptions};
use crate::asset::Asset;
use crate::error::{BinaryError, Result};
use std::collections::HashMap;
use std::path::Path;
#[cfg(feature = "async")]
use tokio::fs;
pub struct BundleLoader {
bundles: HashMap<String, AssetBundle>,
options: BundleLoadOptions,
}
impl BundleLoader {
pub fn new() -> Self {
Self {
bundles: HashMap::new(),
options: BundleLoadOptions::default(),
}
}
pub fn with_options(options: BundleLoadOptions) -> Self {
Self {
bundles: HashMap::new(),
options,
}
}
pub fn load_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<&AssetBundle> {
let path_ref = path.as_ref();
let path_str = path_ref.to_string_lossy().to_string();
if self.bundles.contains_key(&path_str) {
return Ok(self.bundles.get(&path_str).unwrap());
}
let data = std::fs::read(path_ref)
.map_err(|e| BinaryError::generic(format!("Failed to read bundle file: {}", e)))?;
let bundle = BundleParser::from_bytes_with_options(data, self.options.clone())?;
self.bundles.insert(path_str.clone(), bundle);
Ok(self.bundles.get(&path_str).unwrap())
}
pub fn load_from_memory(&mut self, name: String, data: Vec<u8>) -> Result<&AssetBundle> {
if self.bundles.contains_key(&name) {
return Ok(self.bundles.get(&name).unwrap());
}
let bundle = BundleParser::from_bytes_with_options(data, self.options.clone())?;
self.bundles.insert(name.clone(), bundle);
Ok(self.bundles.get(&name).unwrap())
}
#[cfg(feature = "async")]
pub async fn load_from_file_async<P: AsRef<Path>>(&mut self, path: P) -> Result<&AssetBundle> {
let path_ref = path.as_ref();
let path_str = path_ref.to_string_lossy().to_string();
if self.bundles.contains_key(&path_str) {
return Ok(self.bundles.get(&path_str).unwrap());
}
let data = fs::read(path_ref)
.await
.map_err(|e| BinaryError::generic(format!("Failed to read bundle file: {}", e)))?;
let bundle = BundleParser::from_bytes_with_options(data, self.options.clone())?;
self.bundles.insert(path_str.clone(), bundle);
Ok(self.bundles.get(&path_str).unwrap())
}
pub fn get_bundle(&self, name: &str) -> Option<&AssetBundle> {
self.bundles.get(name)
}
pub fn get_bundle_mut(&mut self, name: &str) -> Option<&mut AssetBundle> {
self.bundles.get_mut(name)
}
pub fn unload_bundle(&mut self, name: &str) -> bool {
self.bundles.remove(name).is_some()
}
pub fn unload_all(&mut self) {
self.bundles.clear();
}
pub fn loaded_bundles(&self) -> Vec<&str> {
self.bundles.keys().map(|s| s.as_str()).collect()
}
pub fn memory_usage(&self) -> usize {
self.bundles
.values()
.map(|bundle| bundle.size() as usize)
.sum()
}
pub fn find_assets_by_name(&self, name: &str) -> Vec<(&str, &Asset)> {
let mut results = Vec::new();
for (bundle_name, bundle) in &self.bundles {
for asset in &bundle.assets {
if bundle_name.contains(name) {
results.push((bundle_name.as_str(), asset));
}
}
}
results
}
pub fn find_assets_by_type(&self, _type_id: i32) -> Vec<(&str, &Asset)> {
let mut results = Vec::new();
for (bundle_name, bundle) in &self.bundles {
for asset in &bundle.assets {
results.push((bundle_name.as_str(), asset));
}
}
results
}
pub fn get_statistics(&self) -> LoaderStatistics {
let bundle_count = self.bundles.len();
let total_size = self.memory_usage();
let total_assets: usize = self.bundles.values().map(|b| b.asset_count()).sum();
let total_files: usize = self.bundles.values().map(|b| b.file_count()).sum();
LoaderStatistics {
bundle_count,
total_size,
total_assets,
total_files,
average_bundle_size: if bundle_count > 0 {
total_size / bundle_count
} else {
0
},
}
}
pub fn validate_all(&self) -> Result<()> {
for (name, bundle) in &self.bundles {
bundle.validate().map_err(|e| {
BinaryError::generic(format!("Bundle '{}' validation failed: {}", name, e))
})?;
}
Ok(())
}
pub fn set_options(&mut self, options: BundleLoadOptions) {
self.options = options;
}
pub fn options(&self) -> &BundleLoadOptions {
&self.options
}
}
impl Default for BundleLoader {
fn default() -> Self {
Self::new()
}
}
pub struct BundleResourceManager {
loader: BundleLoader,
dependencies: HashMap<String, Vec<String>>,
reference_counts: HashMap<String, usize>,
}
impl BundleResourceManager {
pub fn new() -> Self {
Self {
loader: BundleLoader::new(),
dependencies: HashMap::new(),
reference_counts: HashMap::new(),
}
}
pub fn load_bundle<P: AsRef<Path>>(
&mut self,
path: P,
dependencies: Vec<String>,
) -> Result<()> {
let path_str = path.as_ref().to_string_lossy().to_string();
for dep in &dependencies {
if !self.loader.bundles.contains_key(dep) {
return Err(BinaryError::generic(format!(
"Dependency '{}' not loaded",
dep
)));
}
*self.reference_counts.entry(dep.clone()).or_insert(0) += 1;
}
self.loader.load_from_file(path)?;
self.dependencies.insert(path_str.clone(), dependencies);
self.reference_counts.insert(path_str, 1);
Ok(())
}
pub fn unload_bundle(&mut self, name: &str) -> Result<()> {
if !self.reference_counts.contains_key(name) {
return Err(BinaryError::generic(format!(
"Bundle '{}' not loaded",
name
)));
}
let ref_count = self.reference_counts.get_mut(name).unwrap();
*ref_count -= 1;
if *ref_count == 0 {
if let Some(deps) = self.dependencies.remove(name) {
for dep in deps {
self.unload_bundle(&dep)?;
}
}
self.loader.unload_bundle(name);
self.reference_counts.remove(name);
}
Ok(())
}
pub fn loader(&self) -> &BundleLoader {
&self.loader
}
pub fn loader_mut(&mut self) -> &mut BundleLoader {
&mut self.loader
}
pub fn get_dependencies(&self, name: &str) -> Option<&Vec<String>> {
self.dependencies.get(name)
}
pub fn get_reference_count(&self, name: &str) -> usize {
self.reference_counts.get(name).copied().unwrap_or(0)
}
}
impl Default for BundleResourceManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct LoaderStatistics {
pub bundle_count: usize,
pub total_size: usize,
pub total_assets: usize,
pub total_files: usize,
pub average_bundle_size: usize,
}
pub fn load_bundle<P: AsRef<Path>>(path: P) -> Result<AssetBundle> {
let data = std::fs::read(path)
.map_err(|e| BinaryError::generic(format!("Failed to read bundle file: {}", e)))?;
BundleParser::from_bytes(data)
}
pub fn load_bundle_from_memory(data: Vec<u8>) -> Result<AssetBundle> {
BundleParser::from_bytes(data)
}
pub fn load_bundle_with_options<P: AsRef<Path>>(
path: P,
options: BundleLoadOptions,
) -> Result<AssetBundle> {
let data = std::fs::read(path)
.map_err(|e| BinaryError::generic(format!("Failed to read bundle file: {}", e)))?;
BundleParser::from_bytes_with_options(data, options)
}
#[cfg(feature = "async")]
pub async fn load_bundle_async<P: AsRef<Path>>(path: P) -> Result<AssetBundle> {
let data = fs::read(path)
.await
.map_err(|e| BinaryError::generic(format!("Failed to read bundle file: {}", e)))?;
BundleParser::from_bytes(data)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_loader_creation() {
let loader = BundleLoader::new();
assert_eq!(loader.loaded_bundles().len(), 0);
assert_eq!(loader.memory_usage(), 0);
}
#[test]
fn test_resource_manager_creation() {
let manager = BundleResourceManager::new();
assert_eq!(manager.loader().loaded_bundles().len(), 0);
}
}