khora_agents/asset_agent/
loader.rs1use anyhow::{anyhow, Result};
18use khora_core::asset::Asset;
19use khora_lanes::asset_lane::AssetLoader;
20use std::{any::Any, collections::HashMap};
21
22trait AnyLoader: Send + Sync {
24 fn load_any(&self, bytes: &[u8]) -> Result<Box<dyn Any + Send>>;
25}
26
27struct LoaderWrapper<A: Asset, L: AssetLoader<A>>(L, std::marker::PhantomData<A>);
29
30impl<A: Asset, L: AssetLoader<A> + Send + Sync> AnyLoader for LoaderWrapper<A, L> {
31 fn load_any(&self, bytes: &[u8]) -> Result<Box<dyn Any + Send>> {
32 let asset: A = self.0.load(bytes).map_err(|e| anyhow!(e.to_string()))?;
34 Ok(Box::new(asset))
36 }
37}
38
39pub(crate) struct LoaderRegistry {
41 loaders: HashMap<String, Box<dyn AnyLoader>>,
42}
43
44impl LoaderRegistry {
45 pub(crate) fn new() -> Self {
47 Self {
48 loaders: HashMap::new(),
49 }
50 }
51
52 pub(crate) fn register<A: Asset>(
54 &mut self,
55 type_name: &str,
56 loader: impl AssetLoader<A> + Send + Sync + 'static,
57 ) {
58 let wrapped = LoaderWrapper(loader, std::marker::PhantomData);
59 self.loaders
60 .insert(type_name.to_string(), Box::new(wrapped));
61 }
62
63 pub(crate) fn load<A: Asset>(&self, type_name: &str, bytes: &[u8]) -> Result<A> {
65 let loader = self
66 .loaders
67 .get(type_name)
68 .ok_or_else(|| anyhow!("No loader registered for asset type '{}'", type_name))?;
69
70 let asset_any = loader.load_any(bytes)?;
71
72 let asset_boxed = asset_any.downcast::<A>().map_err(|_| {
73 anyhow!(
74 "Loader for type '{}' returned a different asset type than requested.",
75 type_name
76 )
77 })?;
78
79 Ok(*asset_boxed)
80 }
81}