Skip to main content

keket_graph/
protocol.rs

1use keket::{
2    database::{
3        inspector::AssetInspector,
4        path::AssetPathStatic,
5        reference::{AssetRef, SmartAssetRef},
6    },
7    protocol::bundle::{
8        BundleWithDependencies, BundleWithDependenciesProcessor, StoreWithDependencies,
9    },
10    third_party::anput::component::Component,
11};
12use std::{
13    error::Error,
14    ops::{Deref, DerefMut},
15};
16
17/// Defines a trait for components that can have asset dependencies.
18///
19/// This trait is useful for `AssetTreeProcessor` protocol to gather asset
20/// dependencies to load.
21pub trait AssetTree: Component {
22    /// Returns an iterator over the asset dependencies of this component.
23    ///
24    /// The dependencies are represented as `AssetPathStatic`, which
25    /// are static paths to the assets that this component depends on.
26    ///
27    /// # Returns
28    /// An iterator over `AssetPathStatic` representing the asset dependencies.
29    fn asset_dependencies(&self) -> impl IntoIterator<Item = AssetPathStatic>;
30}
31
32impl<T: AssetTree> AssetTree for Option<T> {
33    fn asset_dependencies(&self) -> impl IntoIterator<Item = AssetPathStatic> {
34        self.as_ref()
35            .into_iter()
36            .flat_map(|asset| asset.asset_dependencies())
37    }
38}
39
40impl AssetTree for AssetPathStatic {
41    fn asset_dependencies(&self) -> impl IntoIterator<Item = AssetPathStatic> {
42        std::iter::once(self.clone().into_static())
43    }
44}
45
46impl AssetTree for AssetRef {
47    fn asset_dependencies(&self) -> impl IntoIterator<Item = AssetPathStatic> {
48        std::iter::once(self.path().clone().into_static())
49    }
50}
51
52impl AssetTree for SmartAssetRef {
53    fn asset_dependencies(&self) -> impl IntoIterator<Item = AssetPathStatic> {
54        std::iter::once(self.path().clone().into_static())
55    }
56}
57
58/// A wrapper type for components that do not have any asset dependencies.
59///
60/// This type is useful when you want to use a component as an asset tree
61/// but it does not implement `AssetTree` trait or should not give any dependencies.
62#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
63#[repr(transparent)]
64pub struct NoDeps<T: Component>(pub T);
65
66impl<T: Component> Deref for NoDeps<T> {
67    type Target = T;
68
69    fn deref(&self) -> &Self::Target {
70        &self.0
71    }
72}
73
74impl<T: Component> DerefMut for NoDeps<T> {
75    fn deref_mut(&mut self) -> &mut Self::Target {
76        &mut self.0
77    }
78}
79
80impl<T: Component> AssetTree for NoDeps<T> {
81    fn asset_dependencies(&self) -> impl IntoIterator<Item = AssetPathStatic> {
82        []
83    }
84}
85
86/// AssetTreeProcessor is a processor for components that implement the
87/// `AssetTree` trait. It allows deserializing and serializing assets, as well
88/// as processing them with dependencies based on what their `asset_dependencies`
89/// method reports.
90pub struct AssetTreeProcessor<T: AssetTree> {
91    #[allow(clippy::type_complexity)]
92    deserializer: Box<dyn FnMut(Vec<u8>) -> Result<T, Box<dyn Error>> + Send + Sync>,
93    #[allow(clippy::type_complexity)]
94    serializer: Option<Box<dyn FnMut(&T) -> Result<Vec<u8>, Box<dyn Error>> + Send + Sync>>,
95}
96
97impl<T: AssetTree> AssetTreeProcessor<T> {
98    /// Creates a new `AssetTreeProcessor` with the given deserializer.
99    ///
100    /// The deserializer is a function that takes a `Vec<u8>` and returns a
101    /// `Result<T, Box<dyn Error>>`, where `T` is the type of the asset tree component.
102    ///
103    /// # Arguments
104    /// - `deserializer`: A function that deserializes bytes into an asset tree
105    ///   component of type `T`.
106    ///
107    /// # Returns
108    /// A new instance of `AssetTreeProcessor`.
109    pub fn new(
110        deserializer: impl FnMut(Vec<u8>) -> Result<T, Box<dyn Error>> + Send + Sync + 'static,
111    ) -> Self {
112        Self {
113            deserializer: Box::new(deserializer),
114            serializer: None,
115        }
116    }
117
118    /// Sets a serializer for the asset tree component.
119    ///
120    /// This method allows you to provide a function that serializes an asset tree
121    /// component of type `T` into a `Vec<u8>`. The serializer must be a function that
122    /// takes a reference to `T` and returns a `Result<Vec<u8>, Box<dyn Error>>`.
123    ///
124    /// # Arguments
125    /// - `serializer`: A function that serializes an asset tree component of type `T`
126    ///   into a `Vec<u8>`.
127    ///
128    /// # Returns
129    /// A mutable reference to `Self`, allowing for method chaining.
130    pub fn with_serializer(
131        mut self,
132        serializer: impl FnMut(&T) -> Result<Vec<u8>, Box<dyn Error>> + Send + Sync + 'static,
133    ) -> Self {
134        self.serializer = Some(Box::new(serializer));
135        self
136    }
137}
138
139impl<T: AssetTree> BundleWithDependenciesProcessor for AssetTreeProcessor<T> {
140    type Bundle = (T,);
141
142    fn process_bytes(
143        &mut self,
144        bytes: Vec<u8>,
145    ) -> Result<BundleWithDependencies<Self::Bundle>, Box<dyn Error>> {
146        let asset = (self.deserializer)(bytes)?;
147        let dependencies = T::asset_dependencies(&asset)
148            .into_iter()
149            .collect::<Vec<_>>();
150        let mut result = BundleWithDependencies::new((asset,));
151        result.dependencies = dependencies;
152        Ok(result)
153    }
154
155    fn produce_bytes(
156        &mut self,
157        inspector: AssetInspector,
158    ) -> Result<StoreWithDependencies, Box<dyn Error>> {
159        let Some(serializer) = self.serializer.as_mut() else {
160            return Err(format!(
161                "Serializer is not set for AssetTreeProcessor<{}>",
162                std::any::type_name::<T>(),
163            )
164            .into());
165        };
166        let component = inspector.access_checked::<&T>().ok_or_else(|| {
167            format!(
168                "Could not get {} asset component",
169                std::any::type_name::<T>(),
170            )
171        })?;
172        let bytes = (serializer)(component)?;
173        let dependencies = T::asset_dependencies(component)
174            .into_iter()
175            .collect::<Vec<_>>();
176        let mut result = StoreWithDependencies::new(bytes);
177        result.dependencies = dependencies;
178        Ok(result)
179    }
180}