protobuf-convert 0.4.0

Macros for convenient serialization of Rust data structures into/from Protocol Buffers
Documentation
# protobuf-convert

Macros for convenient serialization of Rust data structures into/from Protocol Buffers.

## Introduction

This is a fork of [exonum-derive](https://crates.io/crates/exonum-derive) with
some changes to allow easier integration with other projects, and some new
features.

## Usage

First, add the dependency in `Cargo.toml`:

```toml
protobuf-convert = "0.4.0"
```

Then, define a `ProtobufConvert` trait:

```rust
trait ProtobufConvert {
    /// Type of the protobuf clone of Self
    type ProtoStruct;

    /// Struct -> ProtoStruct
    fn to_pb(&self) -> Self::ProtoStruct;

    /// ProtoStruct -> Struct
    fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error>;
}
```

And to use it, import the trait and the macro:

For example, given the following protobuf:

```protobuf
message Ping {
    fixed64 nonce = 1;
}
```

rust-protobuf will generate the following struct:

```rust
#[derive(PartialEq,Clone,Default)]
#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
pub struct Ping {
    // message fields
    pub nonce: u64,
    // special fields
    #[cfg_attr(feature = "with-serde", serde(skip))]
    pub unknown_fields: ::protobuf::UnknownFields,
    #[cfg_attr(feature = "with-serde", serde(skip))]
    pub cached_size: ::protobuf::CachedSize,
}
```

We may want to convert that struct into a more idiomatic one, and derive more traits.
This is the necessary code:

```rust
// Import trait
use crate::proto::ProtobufConvert;
// Import macro
use protobuf_convert::ProtobufConvert;
// Import module autogenerated by protocol buffers
use crate::proto::schema;

#[derive(ProtobufConvert)]
#[protobuf_convert(source = "schema::Ping")]
struct Ping {
    nonce: u64,
}
```

Note that the `ProtobufConvert` trait must be implemented for all the fields,
see an example implementation for `u64`:

```rust
impl ProtobufConvert for u64 {
    type ProtoStruct = u64;

    fn to_pb(&self) -> Self::ProtoStruct {
        *self
    }

    fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error> {
        Ok(pb)
    }
}
```

Now, converting between `Ping` and `schema::Ping` can be done effortlessly.

### `Enum` support

A more complex example, featuring enums:

```protobuf
message Ping {
    fixed64 nonce = 1;
}
message Pong {
    fixed64 nonce = 1;
}
message Message {
    oneof kind {
        Ping Ping = 1;
        Pong Pong = 2;
    }
}
```

```rust
#[derive(ProtobufConvert)]
#[protobuf_convert(source = "schema::Ping")]
struct Ping {
    nonce: u64,
}
#[derive(ProtobufConvert)]
#[protobuf_convert(source = "schema::Pong")]
struct Pong {
    nonce: u64,
}
#[derive(ProtobufConvert)]
#[protobuf_convert(source = "schema::Message")]
enum Message {
    Ping(Ping),
    Pong(Pong),
}
```

And it just works!

You can also generate `From` and `TryFrom` traits for enum variants. Note that this will not work if enum has variants
with the same field types. To use this feature add `impl_from_trait` attribute.

```rust
#[derive(ProtobufConvert)]
#[protobuf_convert(source = "schema::Message"), impl_from_trait]
enum Message {
    Ping(Ping),
    Pong(Pong),
}
```

`From<Ping>`, `From<Pong>` and also `TryFrom<..>` traits will be generated.

Another attribute that can be used with enum is `rename`. It instructs macro to generate methods with case
specified in attribute param.

```rust
#[derive(ProtobufConvert)]
#[protobuf_convert(source = "schema::Message"), rename(case = "snake_case")]
enum Message {
    Ping(Ping),
    Pong(Pong),
}
```

Currently, only snake case is supported.

### Skipping fields

This macro also supports skipping fields in `struct`s so they are ignored when serializing, i.e they will not be mapped to any field in the schema:

```rust
#[derive(ProtobufConvert)]
#[protobuf_convert(source = "schema::Ping")]
struct Ping {
    pub nonce: u64,
    #[protobuf_convert(skip)]
    my_private_field: u64
}
```

Note that you can only skip fields whose type implements the `Default` trait.

### Overriding conversion rules

This macro also supports serde-like attribute `with` for modules with the custom implementation of `from_pb` and `to_pb` conversions.

`protobuf-convert` will use functions `$module::from_pb` and `$module::to_pb` instead of `ProtobufConvert` trait for the specified field.

```rust
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum CustomId {
    First = 5,
    Second = 15,
    Third = 35,
}

#[derive(Debug, Clone, ProtobufConvert, Eq, PartialEq)]
#[protobuf_convert(source = "proto::SimpleMessage")]
struct CustomMessage {
    #[protobuf_convert(with = "custom_id_pb_convert")]
    id: Option<CustomId>,
    name: String,
}

mod custom_id_pb_convert {
    use super::*;

    pub(super) fn from_pb(pb: u32) -> Result<Option<CustomId>, anyhow::Error> {
        match pb {
            0 => Ok(None),
            5 => Ok(Some(CustomId::First)),
            15 => Ok(Some(CustomId::Second)),
            35 => Ok(Some(CustomId::Third)),
            other => Err(anyhow::anyhow!("Unknown enum discriminant: {}", other)),
        }
    }

    pub(super) fn to_pb(v: &Option<CustomId>) -> u32 {
        match v {
            Some(id) => *id as u32,
            None => 0,
        }
    }
}
```

## See also

* [rust-protobuf]https://github.com/stepancheg/rust-protobuf