brotopuf 0.1.0

A library for (de)serializing structs in protocol buffer wire format
Documentation

brotopuf

brotopuf is a library for serializing and deserializing structs using the protocol buffer wire format.

It does not implement all of the features of protocol buffers. In particular, there is no compiler, and required fields and oneof restrictions are not enforced.

Usage

To make a struct serializable/deserializable, annotate it with the Serialize and/or Deserialize macros, and tag the fields with numbers using the id attribute.

    use brotopuf::Deserialize;
    use brotopuf::DeserializeField;
    use brotopuf::Serialize;

    #[derive(Serialize, Deserialize)]
    struct TestMessage {
        #[id(1)]
        int32: i32,

        #[id(2)]
        int64: i64,
    }

Classes marked with Serialize implement the Serialize trait, and have the serialize method.

pub trait Serialize {
    fn serialize_field(&self, id: u64, pbtype: ProtoType, w: &mut impl Write) -> io::Result<()>;
    fn serialize(&self, w: &mut impl Write) -> io::Result<()>;
}
    let mut v: Vec<u8> = Vec::new();
    s.serialize(&mut v).unwrap();

Likewise for Deserialize:

pub trait Deserialize {
    fn deserialize(&mut self, r: &mut impl Read) -> Result<(), DeserializeError>;
}
        let v: Vec<u8> = vec![
            0x08, 0x96, 0x01, // int32
        ];

        let mut msg = TestMessage {
            int32: 0,
        };

        msg.deserialize(&mut &v[..])?;

        assert_eq!(150, msg.int32);

Numeric types

Numeric types work as you'd expect, using the default varint encoding.

    #[derive(Serialize, Deserialize)]
    struct TestMessage {
        #[id(1)]
        int32: i32,

        #[id(2)]
        int64: i64,

        #[id(3)]
        uint32: u32,

        #[id(4)]
        uint64: u64,

        #[id(7)]
        boolean: bool,

        #[id(12)]
        double: f64,

        #[id(13)]
        float: f32,
    }

string/bytes fields can be represented as String (for string fields that are valid UTF-8), or Vec<u8>, for bytes fields that may contain arbitrary data.

String types

    #[derive(Serialize, Deserialize)]
    struct TestMessage {
        #[id(14)]
        string: String,

        #[id(16)]
        bytes: Vec<u8>,
    }

For numeric types, you can also specify alternate encodings, using the pbtype annotation.

Alternate encodings

    #[derive(Serialize, Deserialize)]
    struct TestMessage {
        #[id(5)]
        #[pbtype(sint32)]
        sint32: i32,

        #[id(6)]
        #[pbtype(sint64)]
        sint64: i64,

        #[id(8)]
        #[pbtype(fixed64)]
        fixed64: u64,

        #[id(9)]
        #[pbtype(sfixed64)]
        sfixed64: i64,

        #[id(10)]
        #[pbtype(fixed32)]
        fixed32: u32,

        #[id(11)]
        #[pbtype(sfixed32)]
        sfixed32: i32,
    }

Enums

C-like enums are also supported, as long as they implement TryFrom for one of the primitive serializable types.

    #[derive(Copy, Clone, Serialize, Deserialize)]
    enum TestEnum {
        VariantZero = 0,
        VariantOne = 1,
        VariantTwo = 2,
    }

    impl TryFrom<u64> for TestEnum {
        type Error = std::io::Error;

        fn try_from(value: u64) -> Result<Self, Self::Error> {
            match value {
                0 => Ok(TestEnum::VariantZero),
                1 => Ok(TestEnum::VariantOne),
                2 => Ok(TestEnum::VariantTwo),
                _ => Err(std::io::Error::new(
                    std::io::ErrorKind::InvalidData,
                    format!("invalid TestEnum value: {}", value),
                )),
            }
        }
    }

    #[derive(Serialize, Deserialize)]
    struct TestMessage {
        #[id(18)]
        enumeration: TestEnum,
    }

Submessages

Submessages are supported.

    #[derive(Serialize, Deserialize)]
    struct SubMessage {
        #[id(1)]
        int32: i32,
    }

    #[derive(Serialize, Deserialize)]
    struct TestMessage {
        #[id(19)]
        submessage: SubMessage,
    }

Repeated fields

Repeated fields can be represented with Vec.

    #[derive(Serialize, Deserialize)]
    struct TestMessage {
        #[id(20)]
        repeated: Vec<u32>,
    }

Optional fields

To make fields optional, wrap them with Option. This prevents them from being serialized, if they are None.

Note that it does not make sense to mark repeated fields as optional..

    #[derive(Serialize)]
    struct TestOptionalMessage {
        #[id(1)]
        int32: Option<i32>,

        #[id(5)]
        #[pbtype(sint32)]
        sint32: Option<i32>,

        #[id(14)]
        string: Option<String>,

        #[id(16)]
        bytes: Option<Vec<u8>>,

        #[id(18)]
        enumeration: Option<TestEnum>,

        #[id(19)]
        submessage: Option<SubMessage>,
    }