Skip to content

Commit

Permalink
Remove more things which are not needed (#145)
Browse files Browse the repository at this point in the history
* Remove required __musli_abort
* Update documentation and SizeHint implementation
* Remove NumberVisitor
* Rename ValueVisitor to UnsizedVisitor
* Fix documentation for derives and hide internal things
* Fix docs
  • Loading branch information
udoprog authored Apr 28, 2024
1 parent 7a6ddde commit df09b31
Show file tree
Hide file tree
Showing 60 changed files with 529 additions and 846 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{matrix.rust}}
- run: cargo build -p musli --all-features
- run: cargo build -p musli --features test
if: matrix.rust != 'stable'
- run: cargo build --all-targets --features test
if: matrix.rust == 'stable'
Expand Down Expand Up @@ -140,7 +140,7 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- uses: Swatinem/rust-cache@v2
- run: cargo doc --lib --no-deps --document-private-items
- run: cargo doc --features test --lib --no-deps --document-private-items
env:
RUSTFLAGS: --cfg doc_cfg
RUSTDOCFLAGS: --cfg doc_cfg -Dwarnings
RUSTDOCFLAGS: --cfg doc_cfg -D warnings
74 changes: 35 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ low-level refreshingly simple [zero-copy serialization][zerocopy].
## Overview

* See [`derives`] to learn how to implement [`Encode`] and [`Decode`].
* See [`data_model`] to learn about the abstract data model of Müsli.
* See [benchmarks] and [size comparisons] to learn about the performance of
this framework.
* See [`tests`] to learn how this library is tested.
Expand Down Expand Up @@ -117,47 +118,38 @@ fn without_musli(storage: &Storage) -> Result<[u8; 8]> {

## Müsli is different from [`serde`]

* We make use of GATs to provide tighter abstractions. GATs were not
available when serde was designed.
* When decoding or encoding we operate by the principle that most things
return either return a [`Decoder`] or [`Encoder`]. This means for example
that field names are not restricted to be strings or indexes, but can be
renamed to [completely arbitrary types][musli-name-type].
* We make less use of the Visitor pattern in certain instances where it's
deemed unnecessary, such as [when decoding collections]. The result is
usually cleaner decode implementations like below.
* We make use of [*moded encoding*](#Modes) allowing the same struct to be
encoded in many different ways.
* We support [detailed tracing] when decoding for rich diagnostics.
* Müsli was designed to support [no-std and no-alloc] environments from the
ground up without compromising on features using a safe and efficient
[scoped allocations].
**Müsli's data model does not speak Rust**. There are no
`serialize_struct_variant` methods which provides metadata about the type
being serialized. The [`Encoder`] and [`Decoder`] traits are agnostic on
this. Compatibility with Rust types is entirely handled using the [`Encode`]
and [`Decode`] derives in combination with [modes](#Modes).

```rust
use musli::Context;
use musli::de::{Decode, Decoder, SequenceDecoder};
**We use GATs** to provide easier to use abstractions. GATs were not
available when serde was designed.

struct MyType {
data: Vec<String>,
}
**Everything is a [`Decoder`] or [`Encoder`]**. Field names are therefore
not limited to be strings or indexes, but can be named to [arbitrary
types][musli-name-type] if needed.

impl<'de, M> Decode<'de, M> for MyType {
fn decode<D>(cx: &D::Cx, decoder: D) -> Result<Self, D::Error>
where
D: Decoder<'de, Mode = M>,
{
decoder.decode_sequence(|seq| {
let mut data = Vec::with_capacity(seq.size_hint().or_default());

while let Some(decoder) = seq.try_decode_next()? {
data.push(decoder.decode()?);
}

Ok(Self { data })
})
}
}
```
**Visitor are only used when needed**. `serde` [completely uses visitors]
when deserializing and the corresponding method is treated as a "hint" to
the underlying format. The deserializer is then free to call any method on
the visitor depending on what the underlying format actually contains. In
Müsli, we swap this around. If the caller wants to decode an arbitrary type
it calls [`decode_any`]. The format can then either signal the appropriate
underlying type or call [`Visitor::visit_unknown`] telling the implementer
that it does not have access to type information.

**We've invented [*moded encoding*](#Modes)** allowing the same Rust types
to be encoded in many different ways with much greater control over how
things encoded. By default we include the [`Binary`] and [`Text`] modes
providing sensible defaults for binary and text-based formats.

**Müsli fully supports [no-std and no-alloc]** from the ground up without
compromising on features using safe and efficient [scoped allocations].

**We support [detailed tracing]** when decoding for much improved
diagnostics of *where* something went wrong.

<br>

Expand Down Expand Up @@ -360,6 +352,8 @@ safety, extensive testing and fuzzing is performed using `miri`. See

[`Binary`]: <https://docs.rs/musli/latest/musli/mode/enum.Binary.html>
[`bincode`]: <https://docs.rs/bincode>
[`data_model`]: <https://docs.rs/musli/latest/musli/help/data_model/index.html>
[`decode_any`]: https://docs.rs/musli/latest/musli/trait.Decoder.html#method.decode_any
[`Decode`]: <https://docs.rs/musli/latest/musli/de/trait.Decode.html>
[`Decoder`]: <https://docs.rs/musli/latest/musli/trait.Decoder.html>
[`derives`]: <https://docs.rs/musli/latest/musli/help/derives/index.html>
Expand All @@ -375,12 +369,14 @@ safety, extensive testing and fuzzing is performed using `miri`. See
[`serde`]: <https://serde.rs>
[`simdutf8`]: <https://docs.rs/simdutf8>
[`tests`]: <https://github.com/udoprog/musli/tree/main/tests>
[`Text`]: <https://docs.rs/musli/latest/musli/mode/enum.Text.html>
[`Visitor::visit_unknown`]: https://docs.rs/musli/latest/musli/de/trait.Visitor.html#method.visit_unknown
[benchmarks]: <https://udoprog.github.io/musli/benchmarks/>
[bit packing]: <https://github.com/udoprog/musli/blob/main/crates/musli/src/descriptive/tag.rs>
[completely uses visitors]: https://docs.rs/serde/latest/serde/trait.Deserializer.html#tymethod.deserialize_u32
[detailed tracing]: <https://udoprog.github.io/rust/2023-05-22/abductive-diagnostics-for-musli.html>
[musli-name-type]: <https://docs.rs/musli/latest/musli/help/derives/index.html#musliname_type-->
[no-std and no-alloc]: <https://github.com/udoprog/musli/blob/main/no-std/examples/no-std-json.rs>
[scoped allocations]: <https://docs.rs/musli-allocator>
[size comparisons]: <https://udoprog.github.io/musli/benchmarks/#size-comparisons>
[when decoding collections]: <https://docs.rs/serde/latest/serde/trait.Deserializer.html#tymethod.deserialize_seq>
[zerocopy]: <https://docs.rs/musli-zerocopy>
51 changes: 25 additions & 26 deletions crates/musli-core/src/de/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::Context;

use super::{
AsDecoder, Decode, DecodeUnsized, DecodeUnsizedBytes, EntriesDecoder, MapDecoder,
NumberVisitor, SequenceDecoder, Skip, ValueVisitor, VariantDecoder, Visitor,
SequenceDecoder, Skip, UnsizedVisitor, VariantDecoder, Visitor,
};

/// Trait governing the implementation of a decoder.
Expand Down Expand Up @@ -229,15 +229,15 @@ pub trait Decoder<'de>: Sized {
/// where
/// D: Decoder<'de>,
/// {
/// decoder.decode_unit()?;
/// decoder.decode_empty()?;
/// Ok(UnitType)
/// }
/// }
/// ```
#[inline]
fn decode_unit(self) -> Result<(), <Self::Cx as Context>::Error> {
fn decode_empty(self) -> Result<(), <Self::Cx as Context>::Error> {
Err(self.cx().message(expecting::unsupported_type(
&expecting::Unit,
&expecting::Empty,
ExpectingWrapper::new(&self),
)))
}
Expand Down Expand Up @@ -898,19 +898,6 @@ pub trait Decoder<'de>: Sized {
)))
}

/// Decode an unknown number using a visitor that can handle arbitrary
/// precision numbers.
#[inline]
fn decode_number<V>(self, visitor: V) -> Result<V::Ok, <Self::Cx as Context>::Error>
where
V: NumberVisitor<'de, Self::Cx>,
{
Err(self.cx().message(expecting::unsupported_type(
&expecting::Number,
ExpectingWrapper::new(&self),
)))
}

/// Decode a fixed-length array.
///
/// # Examples
Expand Down Expand Up @@ -974,7 +961,7 @@ pub trait Decoder<'de>: Sized {
/// use std::fmt;
///
/// use musli::{Context, Decode, Decoder};
/// use musli::de::ValueVisitor;
/// use musli::de::UnsizedVisitor;
/// # struct BytesReference<'de> { data: &'de [u8] }
///
/// impl<'de, M> Decode<'de, M> for BytesReference<'de> {
Expand All @@ -985,7 +972,7 @@ pub trait Decoder<'de>: Sized {
/// {
/// struct Visitor;
///
/// impl<'de, C> ValueVisitor<'de, C, [u8]> for Visitor
/// impl<'de, C> UnsizedVisitor<'de, C, [u8]> for Visitor
/// where
/// C: ?Sized + Context,
/// {
Expand All @@ -1011,7 +998,7 @@ pub trait Decoder<'de>: Sized {
#[inline]
fn decode_bytes<V>(self, visitor: V) -> Result<V::Ok, <Self::Cx as Context>::Error>
where
V: ValueVisitor<'de, Self::Cx, [u8]>,
V: UnsizedVisitor<'de, Self::Cx, [u8]>,
{
Err(self.cx().message(expecting::unsupported_type(
&expecting::Bytes,
Expand Down Expand Up @@ -1041,7 +1028,7 @@ pub trait Decoder<'de>: Sized {
/// use std::fmt;
///
/// use musli::{Context, Decode, Decoder};
/// use musli::de::ValueVisitor;
/// use musli::de::UnsizedVisitor;
/// # struct StringReference<'de> { data: &'de str }
///
/// impl<'de, M> Decode<'de, M> for StringReference<'de> {
Expand All @@ -1052,7 +1039,7 @@ pub trait Decoder<'de>: Sized {
/// {
/// struct Visitor;
///
/// impl<'de, C> ValueVisitor<'de, C, str> for Visitor
/// impl<'de, C> UnsizedVisitor<'de, C, str> for Visitor
/// where
/// C: ?Sized + Context,
/// {
Expand All @@ -1078,7 +1065,7 @@ pub trait Decoder<'de>: Sized {
#[inline]
fn decode_string<V>(self, visitor: V) -> Result<V::Ok, <Self::Cx as Context>::Error>
where
V: ValueVisitor<'de, Self::Cx, str>,
V: UnsizedVisitor<'de, Self::Cx, str>,
{
Err(self.cx().message(expecting::unsupported_type(
&expecting::String,
Expand Down Expand Up @@ -1263,7 +1250,7 @@ pub trait Decoder<'de>: Sized {
F: FnOnce(&mut Self::DecodeSequence) -> Result<O, <Self::Cx as Context>::Error>,
{
Err(self.cx().message(expecting::unsupported_type(
&expecting::Sequence,
&expecting::UnsizedSequence,
ExpectingWrapper::new(&self),
)))
}
Expand Down Expand Up @@ -1301,7 +1288,7 @@ pub trait Decoder<'de>: Sized {
F: FnOnce(&mut Self::DecodeSequenceHint) -> Result<O, <Self::Cx as Context>::Error>,
{
Err(self.cx().message(expecting::unsupported_type(
&expecting::Sequence,
&expecting::SequenceWith(hint.size_hint()),
ExpectingWrapper::new(&self),
)))
}
Expand Down Expand Up @@ -1339,7 +1326,7 @@ pub trait Decoder<'de>: Sized {
F: FnOnce(&mut Self::DecodeMap) -> Result<O, <Self::Cx as Context>::Error>,
{
Err(self.cx().message(expecting::unsupported_type(
&expecting::UnsizedStruct,
&expecting::UnsizedMap,
ExpectingWrapper::new(&self),
)))
}
Expand Down Expand Up @@ -1481,6 +1468,18 @@ pub trait Decoder<'de>: Sized {
)))
}

/// Decode an unknown number using a visitor.
#[inline]
fn decode_number<V>(self, visitor: V) -> Result<V::Ok, <Self::Cx as Context>::Error>
where
V: Visitor<'de, Self::Cx>,
{
Err(self.cx().message(expecting::unsupported_type(
&expecting::Number,
ExpectingWrapper::new(&self),
)))
}

/// Decode dynamically through a [`Visitor`].
#[inline]
fn decode_any<V>(self, visitor: V) -> Result<V::Ok, <Self::Cx as Context>::Error>
Expand Down
2 changes: 1 addition & 1 deletion crates/musli-core/src/de/entries_decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub trait EntriesDecoder<'de>: Sized {
/// Get a size hint for the size of the map being decoded.
#[inline]
fn size_hint(&self) -> SizeHint {
SizeHint::Any
SizeHint::any()
}

/// Try to return the decoder for the first value in the pair.
Expand Down
2 changes: 1 addition & 1 deletion crates/musli-core/src/de/entry_decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub trait EntryDecoder<'de> {
/// Get a size hint for the size of the map being decoded.
#[inline]
fn size_hint(&self) -> SizeHint {
SizeHint::Any
SizeHint::any()
}

/// Return the decoder for the first value in the pair.
Expand Down
2 changes: 1 addition & 1 deletion crates/musli-core/src/de/map_decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub trait MapDecoder<'de>: Sized {
/// Get a size hint of known remaining elements.
#[inline]
fn size_hint(&self) -> SizeHint {
SizeHint::Any
SizeHint::any()
}

/// Decode the next key. This returns `Ok(None)` where there are no more
Expand Down
25 changes: 2 additions & 23 deletions crates/musli-core/src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,6 @@
//! }
//! ```
/// Derive which automatically implements the [`Decode` trait].
///
/// See the [`derives` module] for detailed documentation.
///
/// [`derives` module]: <https://docs.rs/musli/latest/musli/help/derives/index.html>
/// [`Decode` trait]: trait@Decode
///
/// # Examples
///
/// ```
/// use musli::Decode;
///
/// #[derive(Decode)]
/// struct MyType {
/// data: [u8; 128],
/// }
/// ```
#[doc(inline)]
pub use musli_macros::Decode;

mod as_decoder;
Expand Down Expand Up @@ -75,9 +57,6 @@ pub use self::entry_decoder::EntryDecoder;
mod map_decoder;
pub use self::map_decoder::MapDecoder;

mod number_visitor;
pub use self::number_visitor::NumberVisitor;

mod sequence_decoder;
pub use self::sequence_decoder::SequenceDecoder;

Expand All @@ -87,8 +66,8 @@ pub use self::size_hint::SizeHint;
mod skip;
pub use self::skip::Skip;

mod value_visitor;
pub use self::value_visitor::ValueVisitor;
mod unsized_visitor;
pub use self::unsized_visitor::UnsizedVisitor;

mod variant_decoder;
pub use self::variant_decoder::VariantDecoder;
Expand Down
Loading

0 comments on commit df09b31

Please sign in to comment.