Expand description
§Serser – the Rust serialization engine
serser is a serialization/deserialization framework for Rust. It uses a stream of tokens (e.g. U32, Seq, and EndSeq) as the intermediate representation.
§Examples
§Rust To JSON
use serser::derive::*; // The derive macros.
use serser::json::json_from_tokens;
use serser::prelude::*; // Includes the {From,Into]Tokens traits.
#[derive(FromTokens, IntoTokens)]
struct A {
b: u32
}
let original = A { b: 42 };
let mut json = String::new();
json_from_tokens(&mut json, original)?;
assert_eq!(r#"{"b":42}"#, json);§JSON To Rust
use serser::derive::*; // The derive macros.
use serser::json::json_into;
use serser::prelude::*; // Includes the {From,Into]Tokens traits.
#[derive(Debug, Eq, PartialEq)] // For the assert_eq.
#[derive(FromTokens, IntoTokens)]
struct A {
b: u32
}
let got = json_into::<A>(r#"{ "b": 42 }"#)?;
assert_eq!(got, A { b: 42 });§Rust To Rust
This is emulating Clone:
use serser::derive::*; // The derive macros.
use serser::prelude::*; // Includes the {From,Into]Tokens traits.
use serser::TokenVec;
#[derive(Debug, Eq, PartialEq)] // For the assert_eq.
#[derive(FromTokens, IntoTokens)]
struct A {
b: u32
}
let mut tokens = TokenVec::new(); // A buffer, useful for testing.
let original = A { b: 42 };
original.into_tokens(&mut tokens)?; // Writes to `tokens`.
let clone = A::from_tokens(tokens)?; // Reads from `tokens`.
assert_eq!(clone, original);§Compared To serde
The serde crate is the defacto standard for serialization, both for JSON and as used in wasm-bindgen for communicating with the host from WebAssembly.
This crate aims to improve on the serde design in these ways:
- A smaller API, making it easier to write custom (de)serializers.
Only one function needs to be implemented for a sink:
yield_token. Another function can be overridden to provide hints for the next expected token. - A bufferable intermediate representation that can be shared between threads, or stored for later use.
Primarily lightweight, reference-based,
Tokenobjects are used. AnOwningTokenis provided for easy storage and IPC. - A push-based pipeline, inverting the
Iteratorpattern to allow correct lifetimes to be expressed more easily. Tokens only live during the yield_token invocation; if the callee needs to store it, it has to make a copy. This is similar to serde, but avoids the ping-pong between Deserializer, *Access and Visitor. - No reliance on
'staticfor metadata like struct field names.
The aim is for the implementation to be on a par with serde in terms of performance.
The name serser comes from the idea that serialization and deserialization are fundamentally the same thing, just switching perspectives of the source and sink. It is a conversion from one representation to another, through a common data model.
§Processing Model
This shows a flow where a recursive data structure like [42] is being parsed.
The FromTokens trait is a convenience wrapper around FromTokenSink.
It has a blanket implementation for all FromTokenSink.
The FromTokenSink acts as a per-type TokenSink factory. It is implemented for most Rust types.
The IntoTokens trait drives the whole chain. It calls TokenSink::yield_token for every token, and may use TokenSink::expect_tokens to negotiate data types with the sink. The diagram below only shows one lane per trait, but the calls may go to different implementations. It may recursively call other IntoTokens.
The TokenSink is responsible for handling the sinks. This may be writing to a std::fmt::Write, or writing into a Rust data structure buffer. The FromTokenSink::from_sink function knows how to extract the buffered value from a particular TokenSink implementation. A sink may use FromTokenSink to off-load handling of constituent pieces of data to another sink. It owns the sinks it creates, forwards tokens to them, meaning that IntoTokens is oblivious to any subsinks. The TokenSink::expect_tokens function can use FromTokenSink::expect_initial to figure out what a nested data type requires, without creating a sink.
Trait Trait Trait Trait
FromTokens FromTokenSink IntoTokens TokenSink
-----------------------------------------------------------------------
from_tokens
--> new_sink
<..
------------------------> into_tokens
---> yield_token
---> (expect_tokens)
(expect_initial) <-------------------
...................>
new_sink <-------------------
...................>
from_sink <-------------------
...................>
---> ...
|
V
into_tokens
---> yield_token
---> (expect_tokens)
(expect_initial) <-------------------
---> ...
:
V
<........................
--> from_sink
<..Modules§
- derive
- The derive macro that automatically implements the IntoTokens for your structs and enums.
- json
- Parses and serializes JSON.
- prelude
- A module that puts the useful traits into scope.
- test
- Utilities for testing and debugging.
- token
- Definitions of the tokens and metadata.
Structs§
- Token
Vec - A vector of tokens, which can be used as a source or sink.
Enums§
- Token
Error - A simple Error implementation for when nothing special is needed.
Traits§
- Error
- Errors returned from TokenSink must implement this trait.
- From
Token Sink - Provides a way to associate appropriate sinks with Rust data types.
- From
Tokens - Constructs a Rust value from a token source.
- Into
Tokens - Provides a way to associate appropriate sources with Rust data types.
- Token
Sink - A receiver of tokens. This forms the basis for a push-parser. The source side calls Self::yield_token for each simple token. Nested structures are handled by the sink, i.e. any subsink required to parse a data structure is owned by the parent sink, which forwards tokens.
Functions§
- iter_
into_ tokens - Yields the contents of an iterator as a Token::Seq.
- port
- Uses FromTokens and IntoTokens to port from source to sink, using a stream of Token as intermediary representation.