1 unstable release

0.1.0 Sep 23, 2024

#169 in #tokens


Used in serser

MIT license

59KB
1K SLoC

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.

# fn main() -> Result<(), serser::json::WriteError> {
#[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);
# Ok(())
# }

JSON To Rust

use serser::derive::*;                // The derive macros.
use serser::json::json_into;
use serser::prelude::*;               // Includes the {From,Into]Tokens traits.

# fn main() -> Result<(), serser::json::ParseError<serser::TokenError>> {
#[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 });
# Ok(())
# }

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;

# fn main() -> Result<(), serser::TokenError> {
#[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);
# Ok(())
# }

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, Token objects are used. An OwningToken is provided for easy storage and IPC.
  • A push-based pipeline, inverting the Iterator pattern 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 'static for 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.

Dependencies

~225–670KB
~16K SLoC