#enums #tuple #struct #serde #proc-macro

serde-struct-tuple-enum

Procedural macro for deserializing an enum of structs from tuples

2 releases

0.1.1 Jan 19, 2025
0.1.0 Jan 1, 2025

#2283 in Rust patterns

Download history 134/week @ 2024-12-28 43/week @ 2025-01-04 20/week @ 2025-01-11 142/week @ 2025-01-18 7/week @ 2025-01-25

221 downloads per month
Used in 6 crates (via battler-wamp)

MIT license

7KB

serde-struct-tuple-enum

Latest Version

serde-struct-tuple-enum is a utility crate, built initially for battler-wamp. It provides procedural macros to automatically derive serde's Serialize and Deserialize traits for enum types, where each variant of the enum is a struct that is encoded as a tuple of its fields (specifically using serde-struct-tuple).

battler-wamp uses this macro for all WAMP messages, since WAMP messages are encoded as a list, where the first element determines the message variant.


lib.rs:

serde-struct-tuple-enum

serde-struct-tuple-enum is a utility crate, built initially for battler-wamp. It provides procedural macros to automatically derive serde's Serialize and Deserialize traits for enum types, where each variant of the enum is a struct that is encoded as a tuple of its fields (specifically using serde-struct-tuple).

Enums are expected to consist only of unit variants, wrapping a struct that implements serde_struct_tuple::SerializeStructTuple and serde_struct_tuple::DeserializeStructTuple (likely by using the corresponding derive macros).

Each enum variant gets a single "tag" that is encoded as the first element in the tuple. This tag allows the enum variant to be selected for deserializing the rest of the tuple.

This message format directly corresponds to how WAMP messages are encoded.

Example

use std::collections::BTreeMap;

use serde_struct_tuple::{
    DeserializeStructTuple,
    SerializeStructTuple,
};
use serde_struct_tuple_enum::{
    DeserializeStructTupleEnum,
    SerializeStructTupleEnum,
};

#[derive(Debug, Default, PartialEq, Eq, SerializeStructTuple, DeserializeStructTuple)]
struct Foo {
    a: u64,
    b: bool,
    #[serde_struct_tuple(default, skip_serializing_if = Vec::is_empty)]
    c: Vec<u64>,
}

#[derive(Debug, Default, PartialEq, Eq, SerializeStructTuple, DeserializeStructTuple)]
struct Bar {
    a: String,
    #[serde_struct_tuple(default, skip_serializing_if = BTreeMap::is_empty)]
    b: BTreeMap<u64, u64>,
}

#[derive(Debug, PartialEq, Eq, SerializeStructTupleEnum, DeserializeStructTupleEnum)]
#[tag(u64)]
enum Message {
    #[tag = 1]
    Foo(Foo),
    #[tag = 2]
    Bar(Bar),
}

fn main() {
    // Serialization.
    assert_eq!(
        serde_json::to_string(&Message::Foo(Foo {
            a: 123,
            b: true,
            c: Vec::from_iter([7, 8, 9]),
        }))
        .unwrap(),
        r#"[1,123,true,[7,8,9]]"#
    );
    assert_eq!(
        serde_json::to_string(&Message::Bar(Bar {
            a: "hello".to_owned(),
            b: BTreeMap::from_iter([(3, 6), (4, 8)]),
        }))
        .unwrap(),
        r#"[2,"hello",{"3":6,"4":8}]"#
    );

    // Deserialization.
    assert_eq!(
        serde_json::from_str::<Message>(r#"[1,1000,false]"#).unwrap(),
        Message::Foo(Foo {
            a: 1000,
            b: false,
            ..Default::default()
        })
    );
    assert_eq!(
        serde_json::from_str::<Message>(r#"[2,"goodbye",{"1":2,"3":4}]"#).unwrap(),
        Message::Bar(Bar {
            a: "goodbye".to_owned(),
            b: BTreeMap::from_iter([(1, 2), (3, 4)]),
        })
    );
    assert!(
        serde_json::from_str::<Message>(r#"[3]"#)
            .unwrap_err()
            .to_string()
            .contains("expected Message tuple")
    );
}

Dependencies

~0.8–1.3MB
~29K SLoC