brotopuf 0.1.1

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::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 {
    // Serializes as a field inside an enclosing message.
    fn serialize_field(
        &self,
        id: u64,
        pbtype: ProtoType,
        w: &mut impl std::io::Write
    ) -> std::io::Result<()>;

    // Serializes as a standalone message.
    fn serialize(
        &self,
        w: &mut impl std::io::Write
    ) -> std::io::Result<()>;
}
```

`Serialize` types can be written into anything that implements `std::io::Write`.

```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 std::io::Read
    ) -> Result<(), DeserializeError>;
}
```

`Deerialize` types can be read from anything that implements `std::io::Read`.

```rust
let v: Vec<u8> = vec![
    0x08, 0x96, 0x01, // int32
];

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

// from a mutable slice
msg.deserialize(&mut &v[..])?;
assert_eq!(150, msg.int32);

// from an immutable slice
let mut cursor = std::io::Cursor::new(&v[..]);
msg.deserialize(cursor.get_mut())?;
assert_eq!(150, msg.int32);
```

## Numeric types

Numeric types work as you'd expect, using the default `varint` encoding for integers,
and IEEE 754 for floating point numbers.

```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 types

`string`/`bytes` fields can be represented as:
* `String` &mdash; for `string` fields that are valid UTF-8
* `Vec<u8>` &mdash; for `bytes` fields that may contain arbitrary data

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

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

## Alternate encodings

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

```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 deserializable 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>,
}
```