musli_core/
lib.rs

1//! [<img alt="github" src="https://img.shields.io/badge/github-udoprog/musli-8da0cb?style=for-the-badge&logo=github" height="20">](https://github.com/udoprog/musli)
2//! [<img alt="crates.io" src="https://img.shields.io/crates/v/musli-core.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/musli-core)
3//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-musli--core-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/musli-core)
4//!
5//! Core traits for [Müsli].
6//!
7//! [Müsli]: https://docs.rs/musli
8
9#![deny(missing_docs)]
10#![no_std]
11#![cfg_attr(doc_cfg, feature(doc_cfg))]
12
13#[cfg(feature = "alloc")]
14extern crate alloc as rust_alloc;
15
16#[cfg(feature = "std")]
17extern crate std;
18
19mod expecting;
20mod impls;
21mod internal;
22mod never;
23
24pub mod alloc;
25#[doc(inline)]
26pub use self::alloc::Allocator;
27
28mod context;
29#[doc(inline)]
30pub use self::context::Context;
31
32pub mod de;
33#[doc(inline)]
34pub use self::de::{Decode, Decoder};
35
36pub mod en;
37#[doc(inline)]
38pub use self::en::{Encode, Encoder};
39
40pub mod hint;
41pub mod mode;
42
43#[doc(hidden)]
44pub use musli_macros as __macros;
45
46/// This is an attribute macro that must be used when implementing the following traits:
47///
48/// * [`Decoder`]
49/// * [`de::Visitor`][crate::de::Visitor]
50/// * [`de::UnsizedVisitor`][crate::de::UnsizedVisitor]
51/// * [`Encoder`]
52///
53/// It is required to use because these traits might introduce new associated
54/// types in the future, and this is [not yet supported] on a language level in
55/// Rust. So this attribute macro polyfills any missing types automatically.
56///
57/// [not yet supported]: https://rust-lang.github.io/rfcs/2532-associated-type-defaults.html
58///
59/// Note that if the `Cx` or `Mode` associated types are not specified, they
60/// will be defaulted to any type parameters which starts with the uppercase `C`
61/// or `M` respectively if the trait uses them.
62///
63/// # Examples
64///
65/// Implementing `Decoder`:
66///
67/// ```
68/// use std::fmt;
69/// use std::marker::PhantomData;
70///
71/// use musli_core::Context;
72/// use musli_core::de::Decoder;
73///
74/// struct MyDecoder<C, M> {
75///     cx: C,
76///     _marker: PhantomData<M>,
77/// }
78///
79/// #[musli_core::trait_defaults]
80/// impl<'de, C, M> Decoder<'de> for MyDecoder<C, M>
81/// where
82///     C: Context,
83///     M: 'static,
84/// {
85///     #[inline]
86///     fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87///         write!(f, "32-bit unsigned integers")
88///     }
89///
90///     #[inline]
91///     fn decode_u32(self) -> Result<u32, Self::Error> {
92///         Ok(42)
93///     }
94/// }
95/// ```
96///
97/// Implementing `UnsizedVisitor`:
98///
99/// ```
100/// use std::fmt;
101///
102/// use musli_core::Context;
103/// use musli_core::de::UnsizedVisitor;
104///
105/// struct MyVisitor;
106///
107/// #[musli_core::trait_defaults]
108/// impl<'de, C> UnsizedVisitor<'de, C, [u8]> for MyVisitor
109/// where
110///     C: Context,
111/// {
112///     type Ok = ();
113///
114///     #[inline]
115///     fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116///         write!(f, "a reference of bytes")
117///     }
118/// }
119/// ```
120///
121/// Implementing `Visitor`:
122///
123/// ```
124/// use std::fmt;
125///
126/// use musli_core::Context;
127/// use musli_core::de::Visitor;
128///
129/// struct MyVisitor;
130///
131/// #[musli_core::trait_defaults]
132/// impl<'de, C> Visitor<'de, C> for MyVisitor
133/// where
134///     C: Context,
135/// {
136///     type Ok = ();
137///
138///     #[inline]
139///     fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140///         write!(f, "a value that can be decoded into dynamic container")
141///     }
142/// }
143/// ```
144///
145/// Implementing `Encoder`:
146///
147/// ```
148/// use std::fmt;
149/// use std::marker::PhantomData;
150///
151/// use musli_core::Context;
152/// use musli_core::en::Encoder;
153///
154/// struct MyEncoder<'a, C, M> {
155///     value: &'a mut Option<u32>,
156///     cx: C,
157///     _marker: PhantomData<M>,
158/// }
159///
160/// #[musli_core::trait_defaults]
161/// impl<C, M> Encoder for MyEncoder<'_, C, M>
162/// where
163///     C: Context,
164///     M: 'static,
165/// {
166///     #[inline]
167///     fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168///         write!(f, "32-bit unsigned integers")
169///     }
170///
171///     #[inline]
172///     fn encode_u32(self, value: u32) -> Result<(), Self::Error> {
173///         *self.value = Some(value);
174///         Ok(())
175///     }
176/// }
177/// ```
178#[doc(inline)]
179pub use musli_macros::musli_core_trait_defaults as trait_defaults;
180
181/// Internal implementation details of musli.
182///
183/// Using these directly is not supported.
184#[doc(hidden)]
185pub mod __priv {
186    use core::marker::PhantomData;
187
188    pub use crate::alloc::Allocator;
189    use crate::alloc::String;
190    pub use crate::context::Context;
191    pub use crate::de::{
192        AsDecoder, Decode, DecodeBytes, DecodePacked, DecodeTrace, Decoder, EntryDecoder,
193        MapDecoder, SequenceDecoder, TryFastDecode, VariantDecoder,
194    };
195    pub use crate::en::{
196        Encode, EncodeBytes, EncodePacked, EncodeTrace, Encoder, EntryEncoder, MapEncoder,
197        SequenceEncoder, TryFastEncode, VariantEncoder,
198    };
199    pub use crate::hint::MapHint;
200    pub use crate::never::Never;
201
202    pub use ::core::fmt;
203    pub use ::core::mem::{needs_drop, offset_of, size_of};
204    pub use ::core::option::Option;
205    pub use ::core::result::Result;
206
207    #[inline]
208    pub fn default<T>() -> T
209    where
210        T: ::core::default::Default,
211    {
212        ::core::default::Default::default()
213    }
214
215    /// Note that this returns `true` if skipping was unsupported.
216    #[inline]
217    pub fn skip<'de, D>(decoder: D) -> Result<bool, D::Error>
218    where
219        D: Decoder<'de>,
220    {
221        Ok(decoder.try_skip()?.is_unsupported())
222    }
223
224    /// Note that this returns `true` if skipping was unsupported.
225    #[inline]
226    pub fn skip_field<'de, D>(decoder: D) -> Result<bool, D::Error>
227    where
228        D: EntryDecoder<'de>,
229    {
230        skip(decoder.decode_value()?)
231    }
232
233    /// Collect and allocate a string from a [`Display`] implementation.
234    ///
235    /// [`Display`]: fmt::Display
236    #[inline]
237    pub fn collect_string<C>(
238        cx: C,
239        value: impl fmt::Display,
240    ) -> Result<String<C::Allocator>, C::Error>
241    where
242        C: Context,
243    {
244        match crate::alloc::collect_string(cx.alloc(), value) {
245            Ok(string) => Ok(string),
246            Err(error) => Err(cx.message(error)),
247        }
248    }
249
250    /// Construct a map hint from an `Encode` implementation.
251    #[inline]
252    pub fn map_hint<M>(encode: &(impl Encode<M> + ?Sized)) -> impl MapHint + '_
253    where
254        M: 'static,
255    {
256        EncodeMapHint {
257            encode,
258            _marker: PhantomData,
259        }
260    }
261
262    pub(crate) struct EncodeMapHint<'a, T, M>
263    where
264        T: ?Sized,
265    {
266        encode: &'a T,
267        _marker: PhantomData<M>,
268    }
269
270    impl<T, M> MapHint for EncodeMapHint<'_, T, M>
271    where
272        T: ?Sized + Encode<M>,
273    {
274        #[inline]
275        fn get(self) -> Option<usize> {
276            self.encode.size_hint()
277        }
278    }
279
280    /// Helper methods to report errors.
281    pub mod m {
282        use core::fmt;
283
284        use crate::Context;
285
286        /// Report that an invalid variant tag was encountered.
287        #[inline]
288        pub fn invalid_variant_tag<C>(
289            cx: C,
290            type_name: &'static str,
291            tag: impl fmt::Debug,
292        ) -> C::Error
293        where
294            C: Context,
295        {
296            cx.message(format_args!(
297                "Type {type_name} received invalid variant tag {tag:?}"
298            ))
299        }
300
301        /// The value for the given tag could not be collected.
302        #[inline]
303        pub fn expected_tag<C>(cx: C, type_name: &'static str, tag: impl fmt::Debug) -> C::Error
304        where
305            C: Context,
306        {
307            cx.message(format_args!("Type {type_name} expected tag {tag:?}"))
308        }
309
310        /// Trying to decode an uninhabitable type.
311        #[inline]
312        pub fn uninhabitable<C>(cx: C, type_name: &'static str) -> C::Error
313        where
314            C: Context,
315        {
316            cx.message(format_args!(
317                "Type {type_name} cannot be decoded since it's uninhabitable"
318            ))
319        }
320
321        /// Encountered an unsupported field tag.
322        #[inline]
323        pub fn invalid_field_tag<C>(
324            cx: C,
325            type_name: &'static str,
326            tag: impl fmt::Debug,
327        ) -> C::Error
328        where
329            C: Context,
330        {
331            cx.message(format_args!(
332                "Type {type_name} is missing invalid field tag {tag:?}"
333            ))
334        }
335
336        /// Expected another field to decode.
337        #[inline]
338        pub fn expected_field_adjacent<C>(
339            cx: C,
340            type_name: &'static str,
341            tag: impl fmt::Debug,
342            content: impl fmt::Debug,
343        ) -> C::Error
344        where
345            C: Context,
346        {
347            cx.message(format_args!(
348                "Type {type_name} expected adjacent field {tag:?} or {content:?}"
349            ))
350        }
351
352        /// Missing adjacent tag when decoding.
353        #[inline]
354        pub fn missing_adjacent_tag<C>(
355            cx: C,
356            type_name: &'static str,
357            tag: impl fmt::Debug,
358        ) -> C::Error
359        where
360            C: Context,
361        {
362            cx.message(format_args!(
363                "Type {type_name} is missing adjacent tag {tag:?}"
364            ))
365        }
366
367        /// Encountered an unsupported field tag.
368        #[inline]
369        pub fn invalid_field_string_tag<C>(
370            cx: C,
371            type_name: &'static str,
372            field: impl fmt::Debug,
373        ) -> C::Error
374        where
375            C: Context,
376        {
377            cx.message(format_args!(
378                "Type {type_name} received invalid field tag {field:?}"
379            ))
380        }
381
382        /// Missing variant field required to decode.
383        #[inline]
384        pub fn tagged_enum_unsupported<C>(cx: C, type_name: &'static str) -> C::Error
385        where
386            C: Context,
387        {
388            cx.message(format_args!(
389                "Encoding format does not supported decoding type {type_name} as a tagged enum"
390            ))
391        }
392
393        /// Missing variant field required to decode.
394        #[inline]
395        pub fn missing_variant_field<C>(
396            cx: C,
397            type_name: &'static str,
398            tag: impl fmt::Debug,
399        ) -> C::Error
400        where
401            C: Context,
402        {
403            cx.message(format_args!(
404                "Type {type_name} is missing variant field {tag:?}"
405            ))
406        }
407
408        /// Encountered an unsupported variant field.
409        #[inline]
410        pub fn invalid_variant_field_tag<C>(
411            cx: C,
412            type_name: &'static str,
413            variant: impl fmt::Debug,
414            tag: impl fmt::Debug,
415        ) -> C::Error
416        where
417            C: Context,
418        {
419            cx.message(format_args!(
420                "Type {type_name} received invalid variant field tag {tag:?} for variant {variant:?}",
421            ))
422        }
423
424        /// Untagged enum could not be decoded.
425        #[inline]
426        pub fn untagged_mismatch<C>(cx: C, type_name: &'static str) -> C::Error
427        where
428            C: Context,
429        {
430            cx.message(format_args!("No variant of {type_name} could be decoded"))
431        }
432    }
433}