Crate serde_duper

Crate serde_duper 

Source
Expand description

§Serde Duper

Duper is a format which aims to be a human-friendly extension of JSON, with quality-of-life improvements, extra types, and semantic identifiers.

Product({
  product_id: Uuid("1dd7b7aa-515e-405f-85a9-8ac812242609"),
  name: "Wireless Bluetooth Headphones",
  brand: "AudioTech",
  price: Decimal("129.99"),
  dimensions: (18.5, 15.2, 7.8),  // In centimeters
  weight: Kilograms(0.285),
  in_stock: true,
  specifications: {
    battery_life: Duration("30h"),
    noise_cancellation: true,
    connectivity: ["Bluetooth 5.0", "3.5mm Jack"],
  },
  image_thumbnail: Png(b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x64"),
  tags: ["electronics", "audio", "wireless"],
  release_date: Date("2023-11-15"),
  /* Warranty is optional */
  warranty_period: null,
  customer_ratings: {
    latest_review: r#"Absolutely ""astounding""!! 😎"#,
    average: 4.5,
    count: 127,
  },
  created_at: DateTime("2023-11-17T21:50:43+00:00"),
})

This crate allows you to convert between Duper’s text representation and Rust’s native data types, thanks to the serde framework.

Serde provides a powerful way of mapping Duper data to and from Rust data structures largely automatically.

use serde::{Deserialize, Serialize};
use serde_duper::Result;

#[derive(Serialize, Deserialize)]
struct Person {
    name: String,
    age: u8,
    phones: Vec<String>,
}

fn deserialize() -> Result<Person> {
    // Some Duper input data as a &str. Maybe this comes from the user.
    let data = r#"
        Person({
            name: "John Doe",
            age: 43,
            phones: [
                ResidentialPhone("+44 1234567"),
                CellPhone("+44 2345678"),
            ],
        })"#;

    // Parse the string of data into a Person object.
    let p: Person = serde_duper::from_string(data)?;

    // Do things just like with any other Rust data structure.
    println!("Please call {} at the number {}", p.name, p.phones[0]);

    Ok(p)
}

fn serialize(p: Person) -> Result<()> {
    // Serialize the person back into a Duper string.
    let d = serde_duper::to_string(&p)?;

    // Print, write to a file, or send to an HTTP server.
    println!("{}", d);

    Ok(())
}

fn main() {
    let p = deserialize().unwrap();
    serialize(p).unwrap();
}

Any type that implements Serde’s Deserialize trait can be deserialized into a struct like this. This includes built-in Rust standard library type like Vec<T> and HashMap<K, V>, as well as any structs or enums annotated with #[derive(Deserialize)] in the Rust ecosystem.

Conversely, any type that implements Serde’s Serialize trait can be serialized into a string like this. This includes built-in Rust standard library types like Vec<T> and HashMap<K, V>, as well as any structs or enums annotated with #[derive(Serialize)] in the Rust ecosystem.

§Handling bytes

This crate re-exports serde_bytes wrapper types via the bytes module. Prefer using those over the following types if you’d like better byte support:

§Support for identifiers

By default, serialization will attempt to include identifiers for structs and enums, while deserialization will ignore them. It’s possible to customize the emitted identifiers with the #[serde(rename = "...")] attribute.

use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Serialize, Deserialize)]
#[serde(rename = "Status")]
enum UserStatus {
    Disabled,
    PendingApproval,
    Enabled,
}

#[derive(Serialize, Deserialize)]
struct User {
    id: Uuid,
    status: UserStatus,
    last_known_ips: Vec<String>,
}

let u = User {
    id: "314dfe6f-7a76-4c43-80b9-3b0ceb0960c0".parse().unwrap(),
    status: UserStatus::Enabled,
    last_known_ips: vec!["2a02:ec80:700:ed1a::1".to_string()],
};
let d = serde_duper::to_string(&u).unwrap();
println!("{}", d);
// This should print:
//     User({
//       id: "314dfe6f-7a76-4c43-80b9-3b0ceb0960c0",
//       status: Status("Enabled"),
//       last_known_ips: ["2a02:ec80:700:ed1a::1"],
//     })

It’s also possible to remove an identifier with #[serde(rename = "")].

In order to generate identifiers for fields, there are currently three possibilities:

§1. Wrapping your field in a newtype

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[serde(rename = "Status")]
enum UserStatus {
    Disabled,
    PendingApproval,
    Enabled,
}

#[derive(Serialize, Deserialize)]
struct Uuid(uuid::Uuid);

#[derive(Serialize, Deserialize)]
struct User {
    id: Uuid,
    status: UserStatus,
    last_known_ips: Vec<String>,
}

let u = User {
    id: Uuid("314dfe6f-7a76-4c43-80b9-3b0ceb0960c0".parse().unwrap()),
    status: UserStatus::Enabled,
    last_known_ips: vec!["2a02:ec80:700:ed1a::1".to_string()],
};
let d = serde_duper::to_string(&u).unwrap();
println!("{}", d);
// This should print:
//     User({
//       id: Uuid("314dfe6f-7a76-4c43-80b9-3b0ceb0960c0"),
//       status: Status("Enabled"),
//       last_known_ips: ["2a02:ec80:700:ed1a::1"],
//     })

This offers maximum customizability, but requires an extra layer of indirection in your code.

§2. Using a remote (de)serializer

use serde::{Deserialize, Serialize};
use serde_duper::types::DuperUuid;
use uuid::Uuid;

#[derive(Serialize, Deserialize)]
#[serde(rename = "Status")]
enum UserStatus {
    Disabled,
    PendingApproval,
    Enabled,
}

#[derive(Serialize, Deserialize)]
struct User {
    #[serde(with = "DuperUuid")]
    id: Uuid,
    status: UserStatus,
    last_known_ips: Vec<String>,
}

let u = User {
    id: "314dfe6f-7a76-4c43-80b9-3b0ceb0960c0".parse().unwrap(),
    status: UserStatus::Enabled,
    last_known_ips: vec!["2a02:ec80:700:ed1a::1".to_string()],
};
let d = serde_duper::to_string(&u).unwrap();
println!("{}", d);
// This should print:
//     User({
//       id: Uuid("314dfe6f-7a76-4c43-80b9-3b0ceb0960c0"),
//       status: Status("Enabled"),
//       last_known_ips: ["2a02:ec80:700:ed1a::1"],
//     })

The types module provides a simple and quick plug-and-play way of annotating types from std (as well as a few popular third-party crates behind feature flags) with Duper identifiers. It works by providing remote modules that will handle (de)serialization. This is less flexible, but allows you to use the original types directly.

Currently, modules are provided for T and Option<T>.

§3. Using the proc-macro

use serde::{Deserialize, Serialize};
use serde_duper::duper;
use uuid::Uuid;

#[derive(Serialize, Deserialize)]
#[serde(rename = "Status")]
enum UserStatus {
    Disabled,
    PendingApproval,
    Enabled,
}

duper! {
    #[derive(Serialize, Deserialize)]
    struct User {
        #[duper(MyUuid)]
        id: Uuid,
        status: UserStatus,
        #[duper(IpList)]
        last_known_ips: Vec<String>,
    }
}

let u = User {
    id: "314dfe6f-7a76-4c43-80b9-3b0ceb0960c0".parse().unwrap(),
    status: UserStatus::Enabled,
    last_known_ips: vec!["2a02:ec80:700:ed1a::1".to_string()],
};
let d = serde_duper::to_string(&u).unwrap();
println!("{}", d);
// This should print:
//     User({
//       id: MyUuid("314dfe6f-7a76-4c43-80b9-3b0ceb0960c0"),
//       status: Status("Enabled"),
//       last_known_ips: IpList(["2a02:ec80:700:ed1a::1"]),
//     })

This will automatically generate the modules for any type that implements serde_core::Serialize and/or serde_core::Deserialize, not being restricted only to those with a remote (de)serializer module.

This requires the macros feature flag.

Modules§

bytes
Re-export of serde_bytes for better support of bytes in serde_duper.
types

Macros§

duper_serde_module
A helper macro to generate boilerplate for most serializable/deserializable types.

Structs§

Deserializer
Implementation of a deserializer from a DuperValue.
DuperArray
An array (or list) of DuperValues.
DuperBytes
A byte sequence, which may be borrowed or owned.
DuperIdentifier
A Duper identifier: MyIdentifier(...)
DuperKey
A key in a DuperObject.
DuperObject
An object (or map) from DuperKeys to DuperValues.
DuperString
A string, which may be borrowed or owned.
DuperTuple
An tuple of DuperValues.
DuperValue
A Duper value.
Error
This type represents all possible errors that can occur when serializing or deserializing Duper data.
ErrorImpl
This type includes the error kind and message associated with the failure.
Serializer
A structure for serializing Rust values into Duper values.

Enums§

DuperInner
The value contained within a DuperValue.
ErrorKind
The kinds of errors that can happen during serialization and deserialization.

Functions§

from_string
Deserialize an instance of type T from a str slice of Duper text.
from_value
Interpret a DuperValue as an instance of type T.
to_duper
Serialize the given data structure as a Duper value.
to_string
Serialize the given data structure as a String of a Duper value.
to_string_minified
Serialize the given data structure as a String of a Duper value, stripping identifiers from the output.
to_string_pretty
Serialize the given data structure as a String of a Duper value, pretty-printing the output.

Type Aliases§

Result