1 stable release

1.0.0 Sep 25, 2023

#125 in #items

MIT license

30KB
584 lines

ESDE (SE-rialize and DE-serialize library)

The idea is to easily store non-recursive data structures to files and read them back. "Easy" in that context means that the format is understandable by humans and can therefore, in theory, produced from other languages/frameworks as well.

This crate is focused on storing data in binary form, but can be adapted to store it in other formats. It also provides derive macros (Deserialize and Serialize) to derive the corresponding traits for structs and enums.

note on cross-platform: There are no particular guarantees. Most importantly, lengths of slices/vectors are stored as usizes, the length of which can vary from platform to platform.

Items

Items are the units in which data is serialized. This can be anything. I guess in most cases, the data structure is serialized into binary data to store it in a file, then a [u8] will be used.

deserialization

You have an object that provides Items, implementing the Sender trait. This trait is automatically implemented for any std::io::Read, "sending" [u8]s.

Objects that can be deserialized from a stream of given Items implement the Deserialize<Item> trait. Implementations for some primitive types are provided, most others should be derivable with the corresponding and intuitively named derive macro.

If an object is deserialized, its type must be known. In other words: One must know the type before the type can be parsed. No information what type is stored is provided by the library.

serialization

You have an object than accepts Items, implementing the Receiver trait. This trait is automatically implemented for any std::io::Write, receiving [u8]s;

Any type that implements the Serialize<Item> trait can be serialized with any [Receiver<Type = Item>]. Some implementations of primitve types are provided.

example

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Struct {
    another: AnotherStruct,
    an_enum: Enum,
    vector: Vec<u32>,
    option: Option<i64>,
    array: [String; 4],
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct AnotherStruct(u32, u32);

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
enum Enum {
    A,
    B(f64),
    C { x: AnotherStruct, y: u16 },
}

fn main() -> std::io::Result<()> {
    const FILE: &str = "test.bin";

    let example = Struct {
        another: AnotherStruct(69, 420),
        an_enum: Enum::B(69.420),
        vector: vec![69, 420, 1337],
        option: Some(-87),
        array: [
            String::from("one"),
            String::from("two"),
            String::from("three"),
            String::from("four"),
        ],
    };

    {
        let mut file = File::create(FILE)?;
        file.auto_ser(example.clone())?;
        // NOTE Receiver::auto could be used here if File didn't implement Read + Write
    }

    {
        let mut file = File::open(FILE)?;
        let person2: Struct = file.auto_de().map_err(Error::unwrap_sender)?;
        // NOTE Sender::auto could be used here if File didn't implement Read + Write
        println!("person1: {:#?}", example);
        println!("person2: {:#?}", person2);
        assert_eq!(example, person2);
        println!("indeed, they're equal")
    }

    Ok(())
}

planned future features

  • attribute for ignoring fields when (de)serializing, they must implement Default
  • handle generic types

Dependencies

~245–700KB
~17K SLoC