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