Skip to main content

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