musli_common/
macros.rs

1/// Generate extensions assuming an encoding has implemented encode_with.
2#[doc(hidden)]
3#[macro_export]
4macro_rules! encode_with_extensions {
5    ($mode:ident) => {
6        /// Encode the given value to the given [Writer] using the current
7        /// configuration.
8        #[inline]
9        pub fn encode<W, T>(self, writer: W, value: &T) -> Result<(), Error>
10        where
11            W: Writer,
12            T: ?Sized + Encode<$mode>,
13        {
14            let mut buf = $crate::exports::allocator::buffer();
15            let alloc = $crate::exports::allocator::new(&mut buf);
16            let cx = $crate::exports::context::Same::new(&alloc);
17            self.encode_with(&cx, writer, value)
18        }
19
20        /// Encode the given value to the given [Write][io::Write] using the current
21        /// configuration.
22        #[cfg(feature = "std")]
23        #[inline]
24        pub fn to_writer<W, T>(self, write: W, value: &T) -> Result<(), Error>
25        where
26            W: io::Write,
27            T: ?Sized + Encode<$mode>,
28        {
29            let writer = $crate::exports::wrap::wrap(write);
30            self.encode(writer, value)
31        }
32
33        /// Encode the given value to the given [Write][io::Write] using the current
34        /// configuration and context `C`.
35        #[cfg(feature = "std")]
36        #[inline]
37        pub fn to_writer_with<C, W, T>(self, cx: &C, write: W, value: &T) -> Result<(), C::Error>
38        where
39            C: ?Sized + Context<Mode = $mode>,
40            W: io::Write,
41            T: ?Sized + Encode<$mode>,
42        {
43            let writer = $crate::exports::wrap::wrap(write);
44            self.encode_with(cx, writer, value)
45        }
46
47        /// Encode the given value to a [`Vec`] using the current configuration.
48        #[cfg(feature = "alloc")]
49        #[inline]
50        pub fn to_vec<T>(self, value: &T) -> Result<Vec<u8>, Error>
51        where
52            T: ?Sized + Encode<$mode>,
53        {
54            let mut vec = Vec::new();
55            self.encode(&mut vec, value)?;
56            Ok(vec)
57        }
58
59        /// Encode the given value to a [`Vec`] using the current configuration.
60        ///
61        /// This is the same as [`Encoding::to_vec`], but allows for using a
62        /// configurable [`Context`].
63        #[cfg(feature = "alloc")]
64        #[inline]
65        pub fn to_vec_with<C, T>(self, cx: &C, value: &T) -> Result<Vec<u8>, C::Error>
66        where
67            C: ?Sized + Context<Mode = $mode>,
68            T: ?Sized + Encode<$mode>,
69        {
70            let mut vec = Vec::new();
71            self.encode_with(cx, &mut vec, value)?;
72            Ok(vec)
73        }
74
75        /// Encode the given value to a fixed-size bytes using the current
76        /// configuration.
77        #[inline]
78        pub fn to_fixed_bytes<const N: usize, T>(self, value: &T) -> Result<FixedBytes<N>, Error>
79        where
80            T: ?Sized + Encode<$mode>,
81        {
82            let mut buf = $crate::exports::allocator::buffer();
83            let alloc = $crate::exports::allocator::new(&mut buf);
84            let cx = $crate::exports::context::Same::new(&alloc);
85            self.to_fixed_bytes_with(&cx, value)
86        }
87
88        /// Encode the given value to a fixed-size bytes using the current
89        /// configuration.
90        #[inline]
91        pub fn to_fixed_bytes_with<C, const N: usize, T>(
92            self,
93            cx: &C,
94            value: &T,
95        ) -> Result<FixedBytes<N>, C::Error>
96        where
97            C: ?Sized + Context<Mode = $mode>,
98            T: ?Sized + Encode<$mode>,
99        {
100            let mut bytes = FixedBytes::new();
101            self.encode_with(cx, &mut bytes, value)?;
102            Ok(bytes)
103        }
104    };
105}
106
107/// Generate all public encoding helpers.
108#[doc(hidden)]
109#[macro_export]
110macro_rules! encoding_from_slice_impls {
111    ($mode:ident, $decoder_new:path) => {
112        /// Decode the given type `T` from the given slice using the current
113        /// configuration.
114        #[inline]
115        pub fn from_slice<'de, T>(self, bytes: &'de [u8]) -> Result<T, Error>
116        where
117            T: Decode<'de, $mode>,
118        {
119            let mut buf = $crate::exports::allocator::buffer();
120            let alloc = $crate::exports::allocator::new(&mut buf);
121            let cx = $crate::exports::context::Same::new(&alloc);
122            self.from_slice_with(&cx, bytes)
123        }
124
125        /// Decode the given type `T` from the given slice using the current
126        /// configuration.
127        ///
128        /// This is the same as [`Encoding::from_slice`], but allows for using a
129        /// configurable [`Context`].
130        #[inline]
131        pub fn from_slice_with<'de, C, T>(self, cx: &C, bytes: &'de [u8]) -> Result<T, C::Error>
132        where
133            C: ?Sized + Context<Mode = $mode>,
134            T: Decode<'de, $mode>,
135        {
136            let reader = SliceReader::new(bytes);
137            self.decode_with(cx, reader)
138        }
139    };
140}
141
142/// Generate all public encoding helpers.
143#[doc(hidden)]
144#[macro_export]
145macro_rules! encoding_impls {
146    ($mode:ident, $encoder_new:path, $decoder_new:path) => {
147        /// Encode the given value to the given [`Writer`] using the current
148        /// configuration.
149        ///
150        /// This is the same as [`Encoding::encode`] but allows for using a
151        /// configurable [`Context`].
152        #[inline]
153        pub fn encode_with<C, W, T>(self, cx: &C, writer: W, value: &T) -> Result<(), C::Error>
154        where
155            C: ?Sized + Context<Mode = $mode>,
156            W: Writer,
157            T: ?Sized + Encode<$mode>,
158        {
159            T::encode(value, cx, $encoder_new(cx, writer))
160        }
161
162        /// Decode the given type `T` from the given [Reader] using the current
163        /// configuration.
164        ///
165        /// This is the same as [`Encoding::decode`] but allows for using a
166        /// configurable [`Context`].
167        #[inline]
168        pub fn decode_with<'de, C, R, T>(self, cx: &C, reader: R) -> Result<T, C::Error>
169        where
170            C: ?Sized + Context<Mode = $mode>,
171            R: Reader<'de>,
172            T: Decode<'de, $mode>,
173        {
174            T::decode(cx, $decoder_new(cx, reader))
175        }
176
177        /// Decode the given type `T` from the given [Reader] using the current
178        /// configuration.
179        #[inline]
180        pub fn decode<'de, R, T>(self, reader: R) -> Result<T, Error>
181        where
182            R: Reader<'de>,
183            T: Decode<'de, $mode>,
184        {
185            let mut buf = $crate::exports::allocator::buffer();
186            let alloc = $crate::exports::allocator::new(&mut buf);
187            let cx = $crate::exports::context::Same::new(&alloc);
188            self.decode_with(&cx, reader)
189        }
190
191        $crate::encode_with_extensions!($mode);
192    };
193}
194
195#[doc(hidden)]
196#[macro_export]
197macro_rules! test_include_if {
198    (#[musli_value] => $($rest:tt)*) => { $($rest)* };
199    (=> $($_:tt)*) => {};
200}
201
202/// Generate test functions which provides rich diagnostics when they fail.
203#[doc(hidden)]
204#[macro_export]
205#[allow(clippy::crate_in_macro_def)]
206macro_rules! test_fns {
207    ($what:expr $(, $(#[$option:ident])*)?) => {
208        /// Roundtrip encode the given value.
209        #[doc(hidden)]
210        #[track_caller]
211        #[cfg(feature = "test")]
212        pub fn rt<T>(value: T) -> T
213        where
214            T: ::musli::en::Encode + ::musli::de::DecodeOwned + ::core::fmt::Debug + ::core::cmp::PartialEq,
215        {
216            const WHAT: &str = $what;
217            const ENCODING: crate::Encoding = crate::Encoding::new();
218
219            use ::core::any::type_name;
220            use ::alloc::string::ToString;
221
222            struct FormatBytes<'a>(&'a [u8]);
223
224            impl ::core::fmt::Display for FormatBytes<'_> {
225                fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
226                    write!(f, "b\"")?;
227
228                    for b in self.0 {
229                        if b.is_ascii_graphic() {
230                            write!(f, "{}", *b as char)?;
231                        } else {
232                            write!(f, "\\x{b:02x}")?;
233                        }
234                    }
235
236                    write!(f, "\"")?;
237                    Ok(())
238                }
239            }
240
241            let format_error = |cx: &crate::context::SystemContext<_, _>| {
242                use ::alloc::vec::Vec;
243
244                let mut errors = Vec::new();
245
246                for error in cx.errors() {
247                    errors.push(error.to_string());
248                }
249
250                errors.join("\n")
251            };
252
253            let mut buf = crate::allocator::buffer();
254            let alloc = crate::allocator::new(&mut buf);
255            let mut cx = crate::context::SystemContext::new(&alloc);
256            cx.include_type();
257
258            let out = match ENCODING.to_vec_with(&cx, &value) {
259                Ok(out) => out,
260                Err(..) => {
261                    let error = format_error(&cx);
262                    panic!("{WHAT}: {}: failed to encode:\n{error}", type_name::<T>())
263                }
264            };
265
266            $crate::test_include_if! {
267                $($(#[$option])*)* =>
268                let value_decode: ::musli_value::Value = match ENCODING.from_slice_with(&cx, out.as_slice()) {
269                    Ok(decoded) => decoded,
270                    Err(..) => {
271                        let out = FormatBytes(&out);
272                        let error = format_error(&cx);
273                        panic!("{WHAT}: {}: failed to decode to value type:\nBytes:{out}\n{error}", type_name::<T>())
274                    }
275                };
276
277                let value_decoded: T = match ::musli_value::decode_with(&cx, &value_decode) {
278                    Ok(decoded) => decoded,
279                    Err(..) => {
280                        let out = FormatBytes(&out);
281                        let error = format_error(&cx);
282                        panic!("{WHAT}: {}: failed to decode from value type:\nBytes: {out}\nValue: {value_decode:?}\n{error}", type_name::<T>())
283                    }
284                };
285
286                assert_eq!(value_decoded, value, "{WHAT}: {}: musli-value roundtrip does not match", type_name::<T>());
287            }
288
289            let decoded: T = match ENCODING.from_slice_with(&cx, out.as_slice()) {
290                Ok(decoded) => decoded,
291                Err(..) => {
292                    let out = FormatBytes(&out);
293                    let error = format_error(&cx);
294                    panic!("{WHAT}: {}: failed to decode:\nBytes: {out}\n{error}", type_name::<T>())
295                }
296            };
297
298            assert_eq!(decoded, value, "{WHAT}: {}: roundtrip does not match", type_name::<T>());
299
300            decoded
301        }
302
303        /// Encode and then decode the given value once.
304        #[doc(hidden)]
305        #[track_caller]
306        #[cfg(feature = "test")]
307        pub fn decode<'de, T, U>(value: T, out: &'de mut ::alloc::vec::Vec<u8>, _hint: &U) -> U
308        where
309            T: ::musli::en::Encode + ::core::fmt::Debug + ::core::cmp::PartialEq,
310            U: ::musli::de::Decode<'de>,
311        {
312            const WHAT: &str = $what;
313            const ENCODING: crate::Encoding = crate::Encoding::new();
314
315            use ::core::any::type_name;
316            use ::alloc::string::ToString;
317
318            let format_error = |cx: &crate::context::SystemContext<_, _>| {
319                use ::alloc::vec::Vec;
320
321                let mut errors = Vec::new();
322
323                for error in cx.errors() {
324                    errors.push(error.to_string());
325                }
326
327                errors.join("\n")
328            };
329
330            let mut buf = crate::allocator::buffer();
331            let alloc = crate::allocator::new(&mut buf);
332            let mut cx = crate::context::SystemContext::new(&alloc);
333            cx.include_type();
334
335            out.clear();
336
337            match ENCODING.to_writer_with(&cx, &mut *out, &value) {
338                Ok(()) => (),
339                Err(..) => {
340                    let error = format_error(&cx);
341                    panic!("{WHAT}: {}: failed to encode:\n{error}", type_name::<T>())
342                }
343            };
344
345            match ENCODING.from_slice_with(&cx, out) {
346                Ok(decoded) => decoded,
347                Err(error) => {
348                    let error = format_error(&cx);
349                    panic!("{WHAT}: {}: failed to decode:\n{error}", type_name::<T>())
350                }
351            }
352        }
353
354        /// Encode a value to bytes.
355        #[doc(hidden)]
356        #[track_caller]
357        #[cfg(feature = "test")]
358        pub fn to_vec<T>(value: T) -> ::alloc::vec::Vec<u8>
359        where
360            T: ::musli::en::Encode,
361        {
362            const WHAT: &str = $what;
363            const ENCODING: crate::Encoding = crate::Encoding::new();
364
365            use ::core::any::type_name;
366            use ::alloc::string::ToString;
367
368            let format_error = |cx: &crate::context::SystemContext<_, _>| {
369                use ::alloc::vec::Vec;
370
371                let mut errors = Vec::new();
372
373                for error in cx.errors() {
374                    errors.push(error.to_string());
375                }
376
377                errors.join("\n")
378            };
379
380            let mut buf = crate::allocator::buffer();
381            let alloc = crate::allocator::new(&mut buf);
382            let mut cx = crate::context::SystemContext::new(&alloc);
383            cx.include_type();
384
385            match ENCODING.to_vec_with(&cx, &value) {
386                Ok(out) => out,
387                Err(..) => {
388                    let error = format_error(&cx);
389                    panic!("{WHAT}: {}: failed to encode:\n{error}", type_name::<T>())
390                }
391            }
392        }
393    }
394}