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}