limnus_assets_loader/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/limnus
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use limnus_app::prelude::{App, Plugin};
6use limnus_assets::prelude::{Asset, RawWeakId};
7use limnus_local_resource::LocalResourceStorage;
8use limnus_resource::prelude::Resource;
9pub use limnus_resource::ResourceStorage;
10use std::any::{type_name, TypeId};
11use std::collections::HashMap;
12use std::fmt::Debug;
13use std::io;
14use std::io::Error;
15use std::sync::{Arc, Mutex};
16use tracing::debug;
17
18#[derive(Debug)]
19pub enum LoadError {
20    MissingLoader(RawWeakId),
21    ConversionError(ConversionError),
22    Downcast,
23}
24
25#[derive(Debug)]
26pub enum ConversionError {
27    WrongFormat,
28    IoError(io::Error),
29}
30
31impl From<ConversionError> for LoadError {
32    fn from(err: ConversionError) -> Self {
33        Self::ConversionError(err)
34    }
35}
36
37impl From<io::Error> for ConversionError {
38    fn from(value: Error) -> Self {
39        Self::IoError(value)
40    }
41}
42
43pub trait AssetLoader: Send + Sync {
44    type AssetType: Asset + 'static;
45
46    /// # Errors
47    /// TODO: Add more here
48    fn convert_and_insert(
49        &self,
50        id: RawWeakId,
51        octets: &[u8],
52        world: &mut ResourceStorage,
53        local_resource: &mut LocalResourceStorage,
54    ) -> Result<(), ConversionError>;
55}
56
57type TypeIdMap<T> = HashMap<TypeId, T>;
58
59pub trait AnyAssetLoader: Send + Sync {
60    /// # Errors
61    /// TODO: Add more here
62    fn convert_and_insert_erased(
63        &self,
64        id: RawWeakId,
65        octets: &[u8],
66        resources: &mut ResourceStorage,
67        local_resource_storage: &mut LocalResourceStorage,
68    ) -> Result<(), LoadError>;
69
70    fn asset_type_id(&self) -> TypeId;
71}
72
73impl<T> AnyAssetLoader for T
74where
75    T: AssetLoader + 'static,
76{
77    fn convert_and_insert_erased(
78        &self,
79        id: RawWeakId,
80        octets: &[u8],
81        resources: &mut ResourceStorage,
82        local_resource_storage: &mut LocalResourceStorage,
83    ) -> Result<(), LoadError> {
84        self.convert_and_insert(id, octets, resources, local_resource_storage)
85            .map_err(LoadError::from)
86    }
87
88    fn asset_type_id(&self) -> TypeId {
89        TypeId::of::<T::AssetType>()
90    }
91}
92
93#[derive(Resource)]
94pub struct WrappedAssetLoaderRegistry {
95    pub value: Arc<Mutex<AssetLoaderRegistry>>,
96}
97
98impl Debug for WrappedAssetLoaderRegistry {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        write!(f, "WrappedAssetLoaderRegistry")
101    }
102}
103
104#[derive(Default)]
105pub struct AssetLoaderRegistry {
106    loaders: TypeIdMap<Box<dyn AnyAssetLoader>>,
107}
108
109impl Debug for AssetLoaderRegistry {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        write!(f, "AssetLoaderRegistry")
112    }
113}
114
115impl AssetLoaderRegistry {
116    #[must_use]
117    pub fn new() -> Self {
118        Self {
119            loaders: TypeIdMap::new(),
120        }
121    }
122
123    pub fn register_loader<T>(&mut self, loader: T)
124    where
125        T: AssetLoader + 'static,
126    {
127        debug!(
128            asset_type = type_name::<T::AssetType>(),
129            loader = type_name::<T>(),
130            "registering asset loader",
131        );
132        self.loaders
133            .insert(loader.asset_type_id(), Box::new(loader));
134    }
135
136    /// # Errors
137    /// If missing or conversion failed
138    pub fn convert_and_insert(
139        &self,
140        id: RawWeakId,
141        octets: &[u8],
142        resources: &mut ResourceStorage,
143        local_resources: &mut LocalResourceStorage,
144    ) -> Result<(), LoadError> {
145        let loader = self
146            .loaders
147            .get(&id.type_id())
148            .ok_or(LoadError::MissingLoader(id))?;
149
150        loader.convert_and_insert_erased(id, octets, resources, local_resources)
151    }
152}
153
154pub struct AssetLoaderRegistryPlugin;
155
156impl Plugin for AssetLoaderRegistryPlugin {
157    fn build(&self, app: &mut App) {
158        let loader_registry = WrappedAssetLoaderRegistry {
159            value: Arc::new(Mutex::new(AssetLoaderRegistry::new())),
160        };
161        app.insert_resource(loader_registry);
162    }
163}