leafwing_input_manager/user_input/
trait_serde.rs

1//! Serialization and deserialization of user input.
2
3use std::sync::RwLock;
4
5use bevy::app::App;
6use bevy::reflect::GetTypeRegistration;
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use serde_flexitos::ser::require_erased_serialize_impl;
9use serde_flexitos::{serialize_trait_object, Registry};
10use std::sync::LazyLock;
11
12use super::{Axislike, Buttonlike, DualAxislike, TripleAxislike};
13use crate::typetag::{InfallibleMapRegistry, RegisterTypeTag};
14
15/// Registry of deserializers for [`Buttonlike`]s.
16static BUTTONLIKE_REGISTRY: LazyLock<RwLock<InfallibleMapRegistry<dyn Buttonlike>>> =
17    LazyLock::new(|| RwLock::new(InfallibleMapRegistry::new("Buttonlike")));
18
19/// Registry of deserializers for [`Axislike`]s.
20static AXISLIKE_REGISTRY: LazyLock<RwLock<InfallibleMapRegistry<dyn Axislike>>> =
21    LazyLock::new(|| RwLock::new(InfallibleMapRegistry::new("Axislike")));
22
23/// Registry of deserializers for [`DualAxislike`]s.
24static DUAL_AXISLIKE_REGISTRY: LazyLock<RwLock<InfallibleMapRegistry<dyn DualAxislike>>> =
25    LazyLock::new(|| RwLock::new(InfallibleMapRegistry::new("DualAxislike")));
26
27/// Registry of deserializers for [`TripleAxislike`]s.
28static TRIPLE_AXISLIKE_REGISTRY: LazyLock<RwLock<InfallibleMapRegistry<dyn TripleAxislike>>> =
29    LazyLock::new(|| RwLock::new(InfallibleMapRegistry::new("TripleAxislike")));
30
31/// A trait for registering inputs.
32pub trait RegisterUserInput {
33    /// Registers the specified [`Buttonlike`].
34    fn register_buttonlike_input<'de, T>(&mut self) -> &mut Self
35    where
36        T: RegisterTypeTag<'de, dyn Buttonlike> + GetTypeRegistration;
37
38    /// Registers the specified [`Axislike`].
39    fn register_axislike_input<'de, T>(&mut self) -> &mut Self
40    where
41        T: RegisterTypeTag<'de, dyn Axislike> + GetTypeRegistration;
42
43    /// Registers the specified [`DualAxislike`].
44    fn register_dual_axislike_input<'de, T>(&mut self) -> &mut Self
45    where
46        T: RegisterTypeTag<'de, dyn DualAxislike> + GetTypeRegistration;
47
48    /// Registers the specified [`TripleAxislike`].
49    fn register_triple_axislike_input<'de, T>(&mut self) -> &mut Self
50    where
51        T: RegisterTypeTag<'de, dyn TripleAxislike> + GetTypeRegistration;
52}
53
54impl RegisterUserInput for App {
55    fn register_buttonlike_input<'de, T>(&mut self) -> &mut Self
56    where
57        T: RegisterTypeTag<'de, dyn Buttonlike> + GetTypeRegistration,
58    {
59        let mut registry = BUTTONLIKE_REGISTRY.write().unwrap();
60        T::register_typetag(&mut registry);
61        self.register_type::<T>();
62        self
63    }
64
65    fn register_axislike_input<'de, T>(&mut self) -> &mut Self
66    where
67        T: RegisterTypeTag<'de, dyn Axislike> + GetTypeRegistration,
68    {
69        let mut registry = AXISLIKE_REGISTRY.write().unwrap();
70        T::register_typetag(&mut registry);
71        self.register_type::<T>();
72        self
73    }
74
75    fn register_dual_axislike_input<'de, T>(&mut self) -> &mut Self
76    where
77        T: RegisterTypeTag<'de, dyn DualAxislike> + GetTypeRegistration,
78    {
79        let mut registry = DUAL_AXISLIKE_REGISTRY.write().unwrap();
80        T::register_typetag(&mut registry);
81        self.register_type::<T>();
82        self
83    }
84
85    fn register_triple_axislike_input<'de, T>(&mut self) -> &mut Self
86    where
87        T: RegisterTypeTag<'de, dyn TripleAxislike> + GetTypeRegistration,
88    {
89        let mut registry = TRIPLE_AXISLIKE_REGISTRY.write().unwrap();
90        T::register_typetag(&mut registry);
91        self.register_type::<T>();
92        self
93    }
94}
95
96mod buttonlike {
97    use crate::user_input::Buttonlike;
98
99    use super::*;
100
101    impl Serialize for dyn Buttonlike + '_ {
102        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
103        where
104            S: Serializer,
105        {
106            // Check that `Buttonlike` has `erased_serde::Serialize` as a super trait,
107            // preventing infinite recursion at runtime.
108            const fn __check_erased_serialize_super_trait<T: ?Sized + Buttonlike>() {
109                require_erased_serialize_impl::<T>();
110            }
111            serialize_trait_object(serializer, self.reflect_short_type_path(), self)
112        }
113    }
114
115    impl<'de> Deserialize<'de> for Box<dyn Buttonlike> {
116        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
117        where
118            D: Deserializer<'de>,
119        {
120            let registry = BUTTONLIKE_REGISTRY.read().unwrap();
121            registry.deserialize_trait_object(deserializer)
122        }
123    }
124}
125
126mod axislike {
127    use crate::user_input::Axislike;
128
129    use super::*;
130
131    impl Serialize for dyn Axislike + '_ {
132        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
133        where
134            S: Serializer,
135        {
136            // Check that `Axislike` has `erased_serde::Serialize` as a super trait,
137            // preventing infinite recursion at runtime.
138            const fn __check_erased_serialize_super_trait<T: ?Sized + Axislike>() {
139                require_erased_serialize_impl::<T>();
140            }
141            serialize_trait_object(serializer, self.reflect_short_type_path(), self)
142        }
143    }
144
145    impl<'de> Deserialize<'de> for Box<dyn Axislike> {
146        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
147        where
148            D: Deserializer<'de>,
149        {
150            let registry = AXISLIKE_REGISTRY.read().unwrap();
151            registry.deserialize_trait_object(deserializer)
152        }
153    }
154}
155
156mod dualaxislike {
157    use crate::user_input::DualAxislike;
158
159    use super::*;
160
161    impl Serialize for dyn DualAxislike + '_ {
162        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
163        where
164            S: Serializer,
165        {
166            // Check that `DualAxislike` has `erased_serde::Serialize` as a super trait,
167            // preventing infinite recursion at runtime.
168            const fn __check_erased_serialize_super_trait<T: ?Sized + DualAxislike>() {
169                require_erased_serialize_impl::<T>();
170            }
171            serialize_trait_object(serializer, self.reflect_short_type_path(), self)
172        }
173    }
174
175    impl<'de> Deserialize<'de> for Box<dyn DualAxislike> {
176        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
177        where
178            D: Deserializer<'de>,
179        {
180            let registry = DUAL_AXISLIKE_REGISTRY.read().unwrap();
181            registry.deserialize_trait_object(deserializer)
182        }
183    }
184}
185
186mod tripleaxislike {
187    use crate::user_input::TripleAxislike;
188
189    use super::*;
190
191    impl Serialize for dyn TripleAxislike + '_ {
192        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
193        where
194            S: Serializer,
195        {
196            // Check that `TripleAxislike` has `erased_serde::Serialize` as a super trait,
197            // preventing infinite recursion at runtime.
198            const fn __check_erased_serialize_super_trait<T: ?Sized + TripleAxislike>() {
199                require_erased_serialize_impl::<T>();
200            }
201            serialize_trait_object(serializer, self.reflect_short_type_path(), self)
202        }
203    }
204
205    impl<'de> Deserialize<'de> for Box<dyn TripleAxislike> {
206        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
207        where
208            D: Deserializer<'de>,
209        {
210            let registry = TRIPLE_AXISLIKE_REGISTRY.read().unwrap();
211            registry.deserialize_trait_object(deserializer)
212        }
213    }
214}
215
216#[cfg(any(feature = "keyboard", feature = "mouse"))]
217#[cfg(test)]
218mod tests {
219    use crate as leafwing_input_manager;
220    use bevy::prelude::{App, Reflect};
221    use leafwing_input_manager_macros::Actionlike;
222
223    #[derive(Actionlike, Debug, Clone, PartialEq, Eq, Hash, Reflect)]
224    pub enum Action {
225        Foo,
226    }
227
228    fn register_input_deserializers() {
229        let mut app = App::new();
230
231        // Add the plugin to register input deserializers
232        app.add_plugins(crate::prelude::InputManagerPlugin::<Action>::default());
233    }
234
235    #[cfg(feature = "keyboard")]
236    #[test]
237    fn test_button_serde() {
238        use crate::prelude::Buttonlike;
239        use bevy::prelude::KeyCode;
240        use serde_test::{assert_tokens, Token};
241
242        register_input_deserializers();
243
244        let boxed_input: Box<dyn Buttonlike> = Box::new(KeyCode::KeyB);
245        assert_tokens(
246            &boxed_input,
247            &[
248                Token::Map { len: Some(1) },
249                Token::BorrowedStr("KeyCode"),
250                Token::UnitVariant {
251                    name: "KeyCode",
252                    variant: "KeyB",
253                },
254                Token::MapEnd,
255            ],
256        );
257    }
258
259    #[cfg(feature = "mouse")]
260    #[test]
261    fn test_mouse_button_serde() {
262        use bevy::prelude::MouseButton;
263        use serde_test::{assert_tokens, Token};
264
265        use crate::prelude::Buttonlike;
266
267        register_input_deserializers();
268
269        let boxed_input: Box<dyn Buttonlike> = Box::new(MouseButton::Left);
270        assert_tokens(
271            &boxed_input,
272            &[
273                Token::Map { len: Some(1) },
274                Token::BorrowedStr("MouseButton"),
275                Token::UnitVariant {
276                    name: "MouseButton",
277                    variant: "Left",
278                },
279                Token::MapEnd,
280            ],
281        );
282    }
283
284    #[cfg(feature = "mouse")]
285    #[test]
286    fn test_axis_serde() {
287        use crate::prelude::{Axislike, MouseScrollAxis};
288        use serde_test::{assert_tokens, Token};
289
290        register_input_deserializers();
291
292        let boxed_input: Box<dyn Axislike> = Box::new(MouseScrollAxis::Y);
293        assert_tokens(
294            &boxed_input,
295            &[
296                Token::Map { len: Some(1) },
297                Token::BorrowedStr("MouseScrollAxis"),
298                Token::Struct {
299                    name: "MouseScrollAxis",
300                    len: 2,
301                },
302                Token::BorrowedStr("axis"),
303                Token::Enum {
304                    name: "DualAxisType",
305                },
306                Token::Str("Y"),
307                Token::Unit,
308                Token::BorrowedStr("processors"),
309                Token::Seq { len: Some(0) },
310                Token::SeqEnd,
311                Token::StructEnd,
312                Token::MapEnd,
313            ],
314        );
315    }
316
317    #[cfg(feature = "mouse")]
318    #[test]
319    fn test_dual_axis_serde() {
320        use crate::prelude::{DualAxislike, MouseMove};
321        use serde_test::{assert_tokens, Token};
322
323        register_input_deserializers();
324
325        let boxed_input: Box<dyn DualAxislike> = Box::new(MouseMove::default());
326        assert_tokens(
327            &boxed_input,
328            &[
329                Token::Map { len: Some(1) },
330                Token::BorrowedStr("MouseMove"),
331                Token::Struct {
332                    name: "MouseMove",
333                    len: 1,
334                },
335                Token::Str("processors"),
336                Token::Seq { len: Some(0) },
337                Token::SeqEnd,
338                Token::StructEnd,
339                Token::MapEnd,
340            ],
341        );
342    }
343
344    #[cfg(feature = "keyboard")]
345    #[test]
346    fn test_triple_axis_serde() {
347        use crate::prelude::{TripleAxislike, VirtualDPad3D};
348        use bevy::prelude::KeyCode;
349        use serde_test::{assert_tokens, Token};
350
351        register_input_deserializers();
352
353        let boxed_input: Box<dyn TripleAxislike> = Box::new(VirtualDPad3D::new(
354            KeyCode::KeyW,
355            KeyCode::KeyS,
356            KeyCode::KeyA,
357            KeyCode::KeyD,
358            KeyCode::KeyF,
359            KeyCode::KeyB,
360        ));
361        assert_tokens(
362            &boxed_input,
363            &[
364                Token::Map { len: Some(1) },
365                Token::BorrowedStr("VirtualDPad3D"),
366                Token::Struct {
367                    name: "VirtualDPad3D",
368                    len: 6,
369                },
370                Token::Str("up"),
371                Token::Map { len: Some(1) },
372                Token::BorrowedStr("KeyCode"),
373                Token::UnitVariant {
374                    name: "KeyCode",
375                    variant: "KeyW",
376                },
377                Token::MapEnd,
378                Token::Str("down"),
379                Token::Map { len: Some(1) },
380                Token::BorrowedStr("KeyCode"),
381                Token::UnitVariant {
382                    name: "KeyCode",
383                    variant: "KeyS",
384                },
385                Token::MapEnd,
386                Token::Str("left"),
387                Token::Map { len: Some(1) },
388                Token::BorrowedStr("KeyCode"),
389                Token::UnitVariant {
390                    name: "KeyCode",
391                    variant: "KeyA",
392                },
393                Token::MapEnd,
394                Token::Str("right"),
395                Token::Map { len: Some(1) },
396                Token::BorrowedStr("KeyCode"),
397                Token::UnitVariant {
398                    name: "KeyCode",
399                    variant: "KeyD",
400                },
401                Token::MapEnd,
402                Token::Str("forward"),
403                Token::Map { len: Some(1) },
404                Token::BorrowedStr("KeyCode"),
405                Token::UnitVariant {
406                    name: "KeyCode",
407                    variant: "KeyF",
408                },
409                Token::MapEnd,
410                Token::Str("backward"),
411                Token::Map { len: Some(1) },
412                Token::BorrowedStr("KeyCode"),
413                Token::UnitVariant {
414                    name: "KeyCode",
415                    variant: "KeyB",
416                },
417                Token::MapEnd,
418                Token::StructEnd,
419                Token::MapEnd,
420            ],
421        );
422    }
423}