msgpack_tagged/
primitives.rs

1//! Blanket [`MsgpackTagged`] impls for scalar primitives.
2//!
3//! Primitives don't have struct fields or enum variants, so they have no tags
4//! and never go through `serialize_struct` / `deserialize_struct`. Their
5//! `register_into` is a no-op — they exist only to satisfy the `T: MsgpackTagged`
6//! bound that the macro propagates onto every tagged field's type. The
7//! [`Tagged`] shape they advertise is a [`Product`](crate::Product) with empty `fields`,
8//! signalling "no on-the-wire structure of my own."
9//!
10//! `PhantomData<T>` lives here for the same reason: zero-sized, wire-irrelevant,
11//! never registers anything. The struct-field auto-skip in the derive still
12//! drops `PhantomData` fields from the wire, but inside enum variant payloads
13//! (and anywhere else that just needs the bound to hold transitively), this
14//! blanket impl is what makes things compose.
15
16use std::marker::PhantomData;
17
18use crate::{MsgpackTagged, TagRegistry, Tagged};
19
20const LEAF: Tagged = Tagged::empty_product();
21
22macro_rules! impl_msgpack_tagged_for_primitive {
23    ($($t:ty),* $(,)?) => {
24        $(
25            impl MsgpackTagged for $t {
26                const TAGGED: Tagged = LEAF;
27                fn register_into(_reg: &mut TagRegistry) {}
28            }
29        )*
30    };
31}
32
33impl_msgpack_tagged_for_primitive!(
34    (),
35    bool,
36    char,
37    u8,
38    u16,
39    u32,
40    u64,
41    u128,
42    usize,
43    i8,
44    i16,
45    i32,
46    i64,
47    i128,
48    isize,
49    f32,
50    f64,
51    String,
52);
53
54impl<T: 'static> MsgpackTagged for PhantomData<T> {
55    const TAGGED: Tagged = LEAF;
56    fn register_into(_reg: &mut TagRegistry) {}
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    /// The mere fact that these calls type-check proves each primitive has a
64    /// `MsgpackTagged` impl. If any primitive impl gets removed (or an entry
65    /// gets dropped from the macro invocation above), this fails to build.
66    #[test]
67    fn each_primitive_satisfies_the_trait_bound() {
68        fn assert_impl<T: MsgpackTagged>() {}
69        assert_impl::<()>();
70        assert_impl::<bool>();
71        assert_impl::<char>();
72        assert_impl::<u8>();
73        assert_impl::<u16>();
74        assert_impl::<u32>();
75        assert_impl::<u64>();
76        assert_impl::<u128>();
77        assert_impl::<usize>();
78        assert_impl::<i8>();
79        assert_impl::<i16>();
80        assert_impl::<i32>();
81        assert_impl::<i64>();
82        assert_impl::<i128>();
83        assert_impl::<isize>();
84        assert_impl::<f32>();
85        assert_impl::<f64>();
86        assert_impl::<String>();
87    }
88
89    /// Primitives must not put themselves in the registry — only struct/enum
90    /// types that need name → tag lookups do. If a primitive's `register_into`
91    /// ever called `try_insert`, the registry would carry pointless entries
92    /// and the wrapper's strict-on-unknown panic could fire on innocuous types.
93    #[test]
94    fn primitives_dont_register_themselves() {
95        let mut reg = TagRegistry::new();
96        <()>::register_into(&mut reg);
97        <bool>::register_into(&mut reg);
98        <u32>::register_into(&mut reg);
99        <i64>::register_into(&mut reg);
100        <f64>::register_into(&mut reg);
101        <String>::register_into(&mut reg);
102        assert!(reg.is_empty());
103    }
104
105    /// Type parameter that is *not* `MsgpackTagged` — proves the blanket impl's
106    /// bound is `T: 'static` only, never `T: MsgpackTagged`.
107    struct Opaque;
108
109    #[test]
110    fn phantom_data_satisfies_the_trait_bound_without_t_msgpack_tagged() {
111        fn assert_impl<T: MsgpackTagged>() {}
112        assert_impl::<PhantomData<u32>>();
113        assert_impl::<PhantomData<Opaque>>();
114    }
115
116    #[test]
117    fn phantom_data_does_not_register_anything() {
118        let mut reg = TagRegistry::new();
119        <PhantomData<Opaque>>::register_into(&mut reg);
120        assert!(reg.is_empty());
121    }
122}