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](https://protobuf.dev/programming-guides/encoding/).

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.

```rust
    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.

```rust
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<()>;
}
```

```rust
    let mut v: Vec<u8> = Vec::new();
    s.serialize(&mut v).unwrap();
```

Likewise for `Deserialize`:

```rust
pub trait Deserialize {
    fn deserialize(&mut self, r: &mut impl Read) -> Result<(), DeserializeError>;
}
```

```rust
        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.

```rust
    #[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

```rust
    #[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

```rust
    #[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.

```rust
    #[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.

```rust
    #[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`.

```rust
    #[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`..

```rust
    #[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>,
    }
```