swamp_assets_loader/
lib.rs

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