msgpack_tagged/
containers.rs

1//! Blanket [`MsgpackTagged`] impls for stdlib container types.
2//!
3//! Containers don't have struct fields or enum variants of their own, so they
4//! never go through `serialize_struct` / `deserialize_struct` and don't appear
5//! as registry entries. But unlike primitives, their `register_into` is *not*
6//! a no-op: it propagates the recursive walk into the inner type(s) so any
7//! nested `MsgpackTagged` types contained within reach the registry.
8//!
9//! Deliberately omitted: `HashMap` and `HashSet`. Their iteration order is
10//! non-deterministic, which would produce different wire bytes for equal values
11//! across runs. Wire types should use `BTreeMap` / `BTreeSet` instead. If
12//! someone reaches for `HashMap` on the wire, the missing impl is a compile
13//! error — and the `#[diagnostic::on_unimplemented]` note on the trait
14//! definition points them at the `BTreeMap` alternative.
15
16use std::collections::{BTreeMap, BTreeSet};
17
18use crate::{MsgpackTagged, TagRegistry, Tagged};
19
20const LEAF: Tagged = Tagged::empty_product();
21
22impl<T: MsgpackTagged> MsgpackTagged for Vec<T> {
23    const TAGGED: Tagged = LEAF;
24    fn register_into(reg: &mut TagRegistry) {
25        T::register_into(reg);
26    }
27}
28
29impl<T: MsgpackTagged, const N: usize> MsgpackTagged for [T; N] {
30    const TAGGED: Tagged = LEAF;
31    fn register_into(reg: &mut TagRegistry) {
32        T::register_into(reg);
33    }
34}
35
36impl<T: MsgpackTagged> MsgpackTagged for Option<T> {
37    const TAGGED: Tagged = LEAF;
38    fn register_into(reg: &mut TagRegistry) {
39        T::register_into(reg);
40    }
41}
42
43impl<T: MsgpackTagged> MsgpackTagged for Box<T> {
44    const TAGGED: Tagged = LEAF;
45    fn register_into(reg: &mut TagRegistry) {
46        T::register_into(reg);
47    }
48}
49
50impl<K: MsgpackTagged, V: MsgpackTagged> MsgpackTagged for BTreeMap<K, V> {
51    const TAGGED: Tagged = LEAF;
52    fn register_into(reg: &mut TagRegistry) {
53        K::register_into(reg);
54        V::register_into(reg);
55    }
56}
57
58impl<T: MsgpackTagged> MsgpackTagged for BTreeSet<T> {
59    const TAGGED: Tagged = LEAF;
60    fn register_into(reg: &mut TagRegistry) {
61        T::register_into(reg);
62    }
63}
64
65macro_rules! impl_msgpack_tagged_for_tuple {
66    ($($t:ident),+ $(,)?) => {
67        impl<$($t: MsgpackTagged),+> MsgpackTagged for ($($t,)+) {
68            const TAGGED: Tagged = LEAF;
69            fn register_into(reg: &mut TagRegistry) {
70                $($t::register_into(reg);)+
71            }
72        }
73    };
74}
75
76impl_msgpack_tagged_for_tuple!(T0);
77impl_msgpack_tagged_for_tuple!(T0, T1);
78impl_msgpack_tagged_for_tuple!(T0, T1, T2);
79impl_msgpack_tagged_for_tuple!(T0, T1, T2, T3);
80impl_msgpack_tagged_for_tuple!(T0, T1, T2, T3, T4);
81impl_msgpack_tagged_for_tuple!(T0, T1, T2, T3, T4, T5);
82impl_msgpack_tagged_for_tuple!(T0, T1, T2, T3, T4, T5, T6);
83impl_msgpack_tagged_for_tuple!(T0, T1, T2, T3, T4, T5, T6, T7);
84impl_msgpack_tagged_for_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
85impl_msgpack_tagged_for_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
86impl_msgpack_tagged_for_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
87impl_msgpack_tagged_for_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
88
89#[cfg(test)]
90mod tests {
91    use crate::Product;
92
93    use super::*;
94
95    /// A `MsgpackTagged` type that *does* register itself, used to prove
96    /// containers correctly propagate the recursion.
97    struct Foo;
98    impl MsgpackTagged for Foo {
99        const TAGGED: Tagged = Tagged::Product(Product {
100            fields: &[(0, "x")],
101            reserved: &[],
102            allow_unknown_tags: false,
103            tag_order_matches_source: true,
104        });
105        fn register_into(reg: &mut TagRegistry) {
106            reg.try_insert::<Foo>("Foo");
107        }
108    }
109
110    /// Another self-registering type, for two-recursion tests (maps, tuples).
111    struct Bar;
112    impl MsgpackTagged for Bar {
113        const TAGGED: Tagged = Tagged::Product(Product {
114            fields: &[(0, "y")],
115            reserved: &[],
116            allow_unknown_tags: false,
117            tag_order_matches_source: true,
118        });
119        fn register_into(reg: &mut TagRegistry) {
120            reg.try_insert::<Bar>("Bar");
121        }
122    }
123
124    /// Compile-time check that each container has an impl. If any impl above
125    /// is removed, this fails to build.
126    #[test]
127    fn each_container_satisfies_the_trait_bound() {
128        fn assert_impl<T: MsgpackTagged>() {}
129        assert_impl::<Vec<u32>>();
130        assert_impl::<[u32; 4]>();
131        assert_impl::<Option<u32>>();
132        assert_impl::<Box<u32>>();
133        assert_impl::<BTreeMap<String, u32>>();
134        assert_impl::<BTreeSet<u32>>();
135        assert_impl::<(u32,)>();
136        assert_impl::<(u32, u32)>();
137        assert_impl::<(u32, u32, u32)>();
138        assert_impl::<(u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32)>();
139    }
140
141    #[test]
142    fn vec_recurses_into_inner() {
143        let mut reg = TagRegistry::new();
144        <Vec<Foo>>::register_into(&mut reg);
145        assert_eq!(reg.len(), 1);
146        assert!(reg.get("Foo").is_some());
147    }
148
149    #[test]
150    fn option_recurses_into_inner() {
151        let mut reg = TagRegistry::new();
152        <Option<Foo>>::register_into(&mut reg);
153        assert!(reg.get("Foo").is_some());
154    }
155
156    #[test]
157    fn array_recurses_into_inner() {
158        let mut reg = TagRegistry::new();
159        <[Foo; 7]>::register_into(&mut reg);
160        assert!(reg.get("Foo").is_some());
161    }
162
163    #[test]
164    fn box_recurses_into_inner() {
165        let mut reg = TagRegistry::new();
166        <Box<Foo>>::register_into(&mut reg);
167        assert!(reg.get("Foo").is_some());
168    }
169
170    #[test]
171    fn btreemap_recurses_into_key_and_value() {
172        let mut reg = TagRegistry::new();
173        <BTreeMap<Foo, Bar>>::register_into(&mut reg);
174        assert_eq!(reg.len(), 2);
175        assert!(reg.get("Foo").is_some());
176        assert!(reg.get("Bar").is_some());
177    }
178
179    #[test]
180    fn btreeset_recurses_into_inner() {
181        let mut reg = TagRegistry::new();
182        <BTreeSet<Foo>>::register_into(&mut reg);
183        assert!(reg.get("Foo").is_some());
184    }
185
186    #[test]
187    fn tuple_recurses_into_each_element() {
188        let mut reg = TagRegistry::new();
189        <(Foo, Bar)>::register_into(&mut reg);
190        assert!(reg.get("Foo").is_some());
191        assert!(reg.get("Bar").is_some());
192    }
193
194    /// Recursion is transitive: `Vec<Option<Foo>>` reaches `Foo` through two
195    /// container layers, and `Foo` is registered exactly once.
196    #[test]
197    fn nested_containers_recurse_through() {
198        let mut reg = TagRegistry::new();
199        <Vec<Option<Foo>>>::register_into(&mut reg);
200        assert_eq!(reg.len(), 1);
201        assert!(reg.get("Foo").is_some());
202    }
203
204    /// Containers wrapping primitives leave the registry empty — primitives
205    /// don't register themselves, and the container has no entry of its own.
206    #[test]
207    fn container_of_primitive_doesnt_populate_registry() {
208        let mut reg = TagRegistry::new();
209        <Vec<u32>>::register_into(&mut reg);
210        <Option<bool>>::register_into(&mut reg);
211        <BTreeMap<String, u64>>::register_into(&mut reg);
212        <(u32, u64, bool)>::register_into(&mut reg);
213        assert!(reg.is_empty());
214    }
215}