Skip to main content

amethyst_assets/
dyn_format.rs

1//! Dynamic Box<dyn SerializableFormat<T>> serialization.
2//!
3//! This module implements serializers, deserializers and all the required
4//! machinery to allow and loading asset formats from boxed trait types.
5//! This is achieved by using `inventory` crate to store all registered pairs
6//! of asset data types and their formats, and embedding the format name into
7//! the serialization format itself.
8
9use crate::SerializableFormat;
10use serde::{
11    de::{self, DeserializeSeed, Expected, SeqAccess, Visitor},
12    ser::{Serialize, SerializeTupleStruct, Serializer},
13    Deserialize, Deserializer,
14};
15use std::{collections::BTreeMap, marker::PhantomData};
16
17/// A trait for all asset types that have their format types.
18/// Use this as a bound for asset data types when used inside boxed format types intended for deserialization.
19/// registered with `register_format_type` macro.
20///
21///  This trait should never be implemented manually. Use the `register_format_type` macro to register it correctly.
22/// ```ignore
23/// // this must be used exactly once per data type
24/// amethyst_assets::register_format_type!(AudioData);
25///
26/// // this must be used for every Format type impl that can be deserialized dynamically
27/// amethyst_assets::register_format!("WAV", AudioData as WavFormat);
28/// impl Format<AudioData> for WavFormat {
29///     fn name(&self) -> &'static str {
30///         "WAV"
31///     }
32
33///     fn import_simple(&self, bytes: Vec<u8>) -> Result<AudioData, Error> {
34///         Ok(AudioData(bytes))
35///     }
36/// }
37
38/// impl SerializableFormat<AudioData> for WavFormat {}
39/// ```
40pub trait FormatRegisteredData: 'static {
41    // Used by deserialization. This is a private API.
42    #[doc(hidden)]
43    type Registration;
44    #[doc(hidden)]
45    fn get_registration(
46        name: &'static str,
47        deserializer: DeserializeFn<dyn SerializableFormat<Self>>,
48    ) -> Self::Registration;
49    #[doc(hidden)]
50    fn registry() -> &'static Registry<dyn SerializableFormat<Self>>;
51}
52
53// Not public API. Used by macros.
54#[doc(hidden)]
55pub type DeserializeFn<T> =
56    fn(&mut dyn erased_serde::Deserializer<'_>) -> erased_serde::Result<Box<T>>;
57
58// Not public API. Used by macros.
59#[doc(hidden)]
60pub struct Registry<T: ?Sized> {
61    pub map: BTreeMap<&'static str, Option<DeserializeFn<T>>>,
62    pub names: Vec<&'static str>,
63}
64
65pub struct SeqLookupVisitor<'a, T: ?Sized + 'static> {
66    pub expected: &'a dyn Expected,
67    pub registry: &'static Registry<T>,
68}
69
70impl<'de, 'a, T: ?Sized + 'static> Visitor<'de> for SeqLookupVisitor<'a, T> {
71    type Value = DeserializeFn<T>;
72
73    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        Expected::fmt(self.expected, formatter)
75    }
76
77    fn visit_str<E: de::Error>(self, key: &str) -> Result<Self::Value, E> {
78        match self.registry.map.get(key) {
79            Some(Some(value)) => Ok(*value),
80            Some(None) => Err(de::Error::custom(format_args!(
81                "non-unique tag of {}: {:?}",
82                self.expected, key
83            ))),
84            None => Err(de::Error::unknown_variant(key, &self.registry.names)),
85        }
86    }
87}
88
89impl<'de, 'a, T: ?Sized + 'static> DeserializeSeed<'de> for SeqLookupVisitor<'a, T> {
90    type Value = DeserializeFn<T>;
91    fn deserialize<D: Deserializer<'de>>(self, de: D) -> Result<Self::Value, D::Error> {
92        de.deserialize_str(self)
93    }
94}
95
96struct FnApply<T: ?Sized> {
97    pub deserialize_fn: DeserializeFn<T>,
98}
99
100impl<'de, T: ?Sized> DeserializeSeed<'de> for FnApply<T> {
101    type Value = Box<T>;
102
103    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
104    where
105        D: Deserializer<'de>,
106    {
107        let mut erased = erased_serde::Deserializer::erase(deserializer);
108        (self.deserialize_fn)(&mut erased).map_err(de::Error::custom)
109    }
110}
111
112struct FormatVisitor<D: FormatRegisteredData>(PhantomData<D>);
113
114impl<'de, D: FormatRegisteredData> Visitor<'de> for FormatVisitor<D> {
115    type Value = Box<dyn SerializableFormat<D>>;
116
117    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        write!(formatter, "dyn SerializableFormat")
119    }
120
121    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
122    where
123        A: SeqAccess<'de>,
124    {
125        let seq_lookup = SeqLookupVisitor {
126            expected: &self,
127            registry: <D as FormatRegisteredData>::registry(),
128        };
129        let deserialize_fn = match seq.next_element_seed(seq_lookup)? {
130            Some(deserialize_fn) => deserialize_fn,
131            None => {
132                return Err(de::Error::custom("expected tagged Format"));
133            }
134        };
135        seq.next_element_seed(FnApply { deserialize_fn })?
136            .ok_or_else(|| de::Error::invalid_length(1, &self))
137    }
138}
139
140impl<D: FormatRegisteredData> Serialize for dyn SerializableFormat<D> {
141    fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
142        let mut ser = serializer.serialize_tuple_struct("Format", 2)?;
143        ser.serialize_field(self.name())?;
144        ser.serialize_field(&SerializableFormatWrapper(self))?;
145        ser.end()
146    }
147}
148
149struct SerializableFormatWrapper<'a, T: ?Sized>(pub &'a T);
150
151impl<'a, T> Serialize for SerializableFormatWrapper<'a, T>
152where
153    T: ?Sized + erased_serde::Serialize + 'a,
154{
155    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
156    where
157        S: Serializer,
158    {
159        erased_serde::serialize(self.0, serializer)
160    }
161}
162
163impl<'de, D: FormatRegisteredData> Deserialize<'de> for Box<dyn SerializableFormat<D>> {
164    fn deserialize<DE: Deserializer<'de>>(
165        deserializer: DE,
166    ) -> std::result::Result<Self, DE::Error> {
167        deserializer.deserialize_tuple_struct("Format", 2, FormatVisitor::<D>(PhantomData))
168    }
169}
170
171impl<D: FormatRegisteredData> dyn SerializableFormat<D> {
172    // This code is called by `register_format` macro. Considered a private api otherwise.
173    #[doc(hidden)]
174    pub fn format_register(
175        name: &'static str,
176        deserializer: DeserializeFn<Self>,
177    ) -> D::Registration {
178        D::get_registration(name, deserializer)
179    }
180}
181
182/// Register specific asset data types that can be deserialized with dynamic formats.
183/// This is very useful for all assets that have any format types explicitly implemented.
184/// Registered assets are used during loading of nested assets to determine format type
185/// which will be used to deserialize that asset.
186#[macro_export]
187macro_rules! register_format_type {
188    ($($asset_data:ty),*) => {
189        $(
190            #[allow(non_upper_case_globals)]
191            const _register_format_type_impl: () = {
192                $crate::inventory::collect!(AssetFormatRegistration);
193
194                #[doc(hidden)]
195                #[allow(unused)]
196                pub struct AssetFormatRegistration {
197                    name: &'static str,
198                    deserializer: $crate::DeserializeFn<dyn $crate::SerializableFormat<$asset_data>>,
199                }
200
201                impl $crate::FormatRegisteredData for $asset_data {
202                    type Registration = AssetFormatRegistration;
203                    fn get_registration(name: &'static str, deserializer: $crate::DeserializeFn<dyn $crate::SerializableFormat<Self>>) -> Self::Registration {
204                        AssetFormatRegistration { name, deserializer }
205                    }
206                    fn registry() -> &'static $crate::Registry<dyn $crate::SerializableFormat<Self>> {
207                        &REGISTRY
208                    }
209                }
210
211                $crate::lazy_static::lazy_static! {
212                    static ref REGISTRY: $crate::Registry<dyn $crate::SerializableFormat<$asset_data>> = {
213                        let mut map = std::collections::BTreeMap::new();
214                        let mut names = std::vec::Vec::new();
215                        for registered in $crate::inventory::iter::<AssetFormatRegistration> {
216                            match map.entry(registered.name) {
217                                std::collections::btree_map::Entry::Vacant(entry) => {
218                                    entry.insert(std::option::Option::Some(registered.deserializer));
219                                }
220                                std::collections::btree_map::Entry::Occupied(mut entry) => {
221                                    entry.insert(std::option::Option::None);
222                                }
223                            }
224                            names.push(registered.name);
225                        }
226                        names.sort_unstable();
227                        $crate::Registry { map, names }
228                    };
229                }
230            };
231        )*
232    }
233}
234
235/// Register a dynamically deserializable format for given asset data type.
236/// Note that provided asset data type must also be registered using `register_format_type` macro.
237///
238/// ```ignore
239/// amethyst_assets::register_format!("WAV", WavFormat as AudioData);
240/// ```
241///
242/// The provided name literal (`"WAV"` in example) must be identical to the one returned from `fn name` implementation.
243/// This is required for right deserialization to be determined correctly. This parameter might be removed
244/// in the future when some macro limitations will be worked around.
245///
246/// The `amethyst_assets` crate must be in scope in order to use that macro.
247/// You can also specify name for the crate as additional first parameter.
248///
249/// ```ignore
250/// amethyst_assets::register_format!(renamed_assets_crate; "WAV", WavFormat as AudioData);
251/// ```
252#[macro_export]
253macro_rules! register_format {
254    ($name:literal, $format:ty as $data:ty) => {
255        $crate::register_format!(amethyst_assets; $name, $format as $data);
256    };
257    ($name:literal, $format:ty as $data:ty) => {
258        $crate::register_format!(amethyst_assets; $name, $format as $data);
259    };
260    ($krate:ident; $name:literal, $format:ty as $data:ty) => {
261        $crate::inventory::submit!{
262            #![crate = $krate]
263            <dyn $crate::SerializableFormat<$data>>::format_register(
264                $name,
265                |deserializer| std::result::Result::Ok(
266                    std::boxed::Box::new(
267                        $crate::erased_serde::deserialize::<$format>(deserializer)?
268                    ),
269                ),
270            )
271        }
272        impl $crate::SerializableFormat<$data> for $format {}
273    };
274}
275
276#[cfg(test)]
277mod tests {
278    use serde;
279    use serde_json;
280
281    use super::*;
282    use crate as amethyst_assets;
283    use crate::Format;
284    use amethyst_error::Error;
285
286    #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
287    struct TestPrefab {
288        test: Box<dyn SerializableFormat<TestData>>,
289    }
290
291    #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
292    struct TestData(String);
293    register_format_type!(TestData);
294
295    #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
296    struct TestFormat(String);
297    register_format!("FOO", TestFormat as TestData);
298
299    impl Format<TestData> for TestFormat {
300        fn name(&self) -> &'static str {
301            "FOO"
302        }
303
304        fn import_simple(&self, _bytes: Vec<u8>) -> Result<TestData, Error> {
305            // Just returning the stored string in order to assert against something meaningful.
306            Ok(TestData(self.0.clone()))
307        }
308    }
309
310    #[test]
311    fn test_format_serialize() {
312        let prefab = TestPrefab {
313            test: Box::new(TestFormat("test string".to_owned())),
314        };
315
316        // Tests that serializing and deserializing dyn SerializableFormat yields the same data.
317        let serialized_prefab = serde_json::to_value(&prefab).unwrap();
318        let deserialized_prefab: TestPrefab = serde_json::from_value(serialized_prefab).unwrap();
319        assert_eq!(
320            prefab.test.import_simple(Vec::new()).unwrap(),
321            deserialized_prefab.test.import_simple(Vec::new()).unwrap()
322        );
323    }
324}