use crate::{
database::{
handle::{AssetDependency, AssetHandle},
inspector::AssetInspector,
path::{AssetPath, AssetPathStatic},
},
fetch::AssetAwaitsResolution,
protocol::AssetProtocol,
store::AssetAwaitsStoring,
};
use anput::{
bundle::{Bundle, DynamicBundle},
world::World,
};
use std::error::Error;
pub struct BundleWithDependencies<B: Bundle> {
pub bundle: B,
pub dependencies: Vec<AssetPathStatic>,
}
impl<B: Bundle> BundleWithDependencies<B> {
pub fn new(bundle: B) -> Self {
Self {
bundle,
dependencies: Default::default(),
}
}
pub fn dependency(mut self, path: impl Into<AssetPathStatic>) -> Self {
self.dependencies.push(path.into());
self
}
pub fn maybe_dependency(mut self, path: Option<impl Into<AssetPathStatic>>) -> Self {
if let Some(path) = path {
self.dependencies.push(path.into());
}
self
}
pub fn dependencies(mut self, paths: impl IntoIterator<Item = AssetPathStatic>) -> Self {
self.dependencies.extend(paths);
self
}
}
impl<B: Bundle> From<B> for BundleWithDependencies<B> {
fn from(bundle: B) -> Self {
Self {
bundle,
dependencies: Default::default(),
}
}
}
impl<B: Bundle> From<(B, Vec<AssetPathStatic>)> for BundleWithDependencies<B> {
fn from((bundle, dependencies): (B, Vec<AssetPathStatic>)) -> Self {
Self {
bundle,
dependencies,
}
}
}
pub struct StoreWithDependencies {
pub bytes: Vec<u8>,
pub dependencies: Vec<AssetPathStatic>,
}
impl StoreWithDependencies {
pub fn new(bytes: Vec<u8>) -> Self {
Self {
bytes,
dependencies: Default::default(),
}
}
pub fn dependency(mut self, path: impl Into<AssetPathStatic>) -> Self {
self.dependencies.push(path.into());
self
}
pub fn maybe_dependency(mut self, path: Option<impl Into<AssetPathStatic>>) -> Self {
if let Some(path) = path {
self.dependencies.push(path.into());
}
self
}
pub fn dependencies(mut self, paths: impl IntoIterator<Item = AssetPathStatic>) -> Self {
self.dependencies.extend(paths);
self
}
}
impl From<Vec<u8>> for StoreWithDependencies {
fn from(bytes: Vec<u8>) -> Self {
Self {
bytes,
dependencies: Default::default(),
}
}
}
impl From<(Vec<u8>, Vec<AssetPathStatic>)> for StoreWithDependencies {
fn from((bytes, dependencies): (Vec<u8>, Vec<AssetPathStatic>)) -> Self {
Self {
bytes,
dependencies,
}
}
}
pub trait BundleWithDependenciesProcessor: Send + Sync {
type Bundle: Bundle;
#[allow(unused_variables)]
fn extract_bundle_from_path(&self, path: &AssetPath) -> Result<DynamicBundle, Box<dyn Error>> {
Ok(Default::default())
}
fn rewrite_path(&self, path: AssetPathStatic) -> Result<AssetPathStatic, Box<dyn Error>> {
Ok(path)
}
fn process_bytes(
&mut self,
bytes: Vec<u8>,
) -> Result<BundleWithDependencies<Self::Bundle>, Box<dyn Error>>;
#[allow(unused_variables)]
fn produce_bytes(
&mut self,
inspector: AssetInspector,
) -> Result<StoreWithDependencies, Box<dyn Error>> {
Err("Processor does not support producing bytes from assets".into())
}
#[allow(unused_variables)]
fn maintain(&mut self, storage: &mut World) -> Result<(), Box<dyn Error>> {
Ok(())
}
}
impl<B, F> BundleWithDependenciesProcessor for F
where
B: Bundle,
F: FnMut(Vec<u8>) -> Result<BundleWithDependencies<B>, Box<dyn Error>> + Send + Sync,
{
type Bundle = B;
fn process_bytes(
&mut self,
bytes: Vec<u8>,
) -> Result<BundleWithDependencies<Self::Bundle>, Box<dyn Error>> {
self(bytes)
}
}
impl<B, F, G> BundleWithDependenciesProcessor for (F, G)
where
B: Bundle,
F: FnMut(Vec<u8>) -> Result<BundleWithDependencies<B>, Box<dyn Error>> + Send + Sync,
G: FnMut(AssetInspector) -> Result<StoreWithDependencies, Box<dyn Error>> + Send + Sync,
{
type Bundle = B;
fn process_bytes(
&mut self,
bytes: Vec<u8>,
) -> Result<BundleWithDependencies<Self::Bundle>, Box<dyn Error>> {
(self.0)(bytes)
}
fn produce_bytes(
&mut self,
inspector: AssetInspector,
) -> Result<StoreWithDependencies, Box<dyn Error>> {
(self.1)(inspector)
}
}
pub struct BundleAssetProtocol<Processor: BundleWithDependenciesProcessor> {
name: String,
processor: Processor,
}
impl<Processor: BundleWithDependenciesProcessor> BundleAssetProtocol<Processor> {
pub fn new(name: impl ToString, processor: Processor) -> Self {
Self {
name: name.to_string(),
processor,
}
}
}
impl<Processor: BundleWithDependenciesProcessor> AssetProtocol for BundleAssetProtocol<Processor> {
fn name(&self) -> &str {
&self.name
}
fn extract_bundle_from_path(&self, path: &AssetPath) -> Result<DynamicBundle, Box<dyn Error>> {
self.processor.extract_bundle_from_path(path)
}
fn rewrite_path(&self, path: AssetPathStatic) -> Result<AssetPathStatic, Box<dyn Error>> {
self.processor.rewrite_path(path)
}
fn process_bytes(
&mut self,
handle: AssetHandle,
storage: &mut World,
bytes: Vec<u8>,
) -> Result<(), Box<dyn Error>> {
let BundleWithDependencies {
bundle,
dependencies,
} = self.processor.process_bytes(bytes)?;
storage.insert(handle.entity(), bundle)?;
for path in dependencies {
let entity = storage.spawn((path, AssetAwaitsResolution))?;
storage.relate::<true, _>(AssetDependency, handle.entity(), entity)?;
}
Ok(())
}
fn produce_bytes(
&mut self,
handle: AssetHandle,
storage: &mut World,
) -> Result<Vec<u8>, Box<dyn Error>> {
let StoreWithDependencies {
bytes,
dependencies,
} = self
.processor
.produce_bytes(AssetInspector::new_raw(storage, handle.entity()))?;
for path in dependencies {
if let Some(entity) = storage.find_by::<true, _>(&path) {
storage.insert(entity, (AssetAwaitsStoring,))?;
}
}
Ok(bytes)
}
fn maintain(&mut self, storage: &mut World) -> Result<(), Box<dyn Error>> {
self.processor.maintain(storage)
}
}