Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/witnet/protobuf-convert
Macros for convenient serialization of Rust data structures into/from Protocol Buffers 3
https://github.com/witnet/protobuf-convert
buffers macro protobuf protocol rust schema serialization
Last synced: about 21 hours ago
JSON representation
Macros for convenient serialization of Rust data structures into/from Protocol Buffers 3
- Host: GitHub
- URL: https://github.com/witnet/protobuf-convert
- Owner: witnet
- License: apache-2.0
- Created: 2019-02-13T14:08:10.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2022-09-15T10:08:13.000Z (about 2 years ago)
- Last Synced: 2024-08-03T01:38:54.959Z (3 months ago)
- Topics: buffers, macro, protobuf, protocol, rust, schema, serialization
- Language: Rust
- Size: 46.9 KB
- Stars: 34
- Watchers: 10
- Forks: 6
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# 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;
}
```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 {
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`, `From` 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,
name: String,
}mod custom_id_pb_convert {
use super::*;pub(super) fn from_pb(pb: u32) -> Result, 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) -> u32 {
match v {
Some(id) => *id as u32,
None => 0,
}
}
}
```## See also
* [rust-protobuf](https://github.com/stepancheg/rust-protobuf)