#protobuf #macro #buffer #protocols #serialization #structures #data

protobuf-convert

方便将 Rust 数据结构序列化为 Protocol Buffers 的宏

7 个版本 (4 个重大变更)

0.5.0 2022 年 9 月 15 日
0.4.0 2020 年 3 月 27 日
0.3.0 2019 年 12 月 12 日
0.2.1 2019 年 12 月 4 日
0.1.0 2019 年 2 月 14 日

#1707 in 过程宏

Download history 94/week @ 2024-03-11 85/week @ 2024-03-18 257/week @ 2024-03-25 149/week @ 2024-04-01 52/week @ 2024-04-08 99/week @ 2024-04-15 87/week @ 2024-04-22 82/week @ 2024-04-29 71/week @ 2024-05-06 76/week @ 2024-05-13 90/week @ 2024-05-20 61/week @ 2024-05-27 78/week @ 2024-06-03 59/week @ 2024-06-10 54/week @ 2024-06-17 56/week @ 2024-06-24

每月下载量 250
15 个 Crates 中使用 (通过 exonum-proto)

Apache-2.0 协议

30KB
483

protobuf-convert

方便将 Rust 数据结构序列化为 Protocol Buffers 的宏。

简介

这是从 exonum-derive 分支出来的,进行了一些修改以简化与其他项目的集成,并添加了一些新功能。

使用方法

首先,在 Cargo.toml 中添加依赖项

protobuf-convert = "0.4.0"

然后,定义一个 ProtobufConvert 特征

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>;
}

要使用它,请导入特征和宏

例如,给定以下 protobuf

message Ping {
    fixed64 nonce = 1;
}

rust-protobuf 将生成以下结构体

#[derive(PartialEq,Clone,Default)]
pub struct Ping {
    // message fields
    pub nonce: u64,
    // special fields
    pub special_fields: ::protobuf::SpecialFields,
}

我们可能希望将该结构体转换为更符合 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,
}

请注意,必须为所有字段实现 ProtobufConvert 特征,请参阅 u64 的示例实现

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)
    }
}

现在,在 Pingschema::Ping 之间进行转换可以轻松完成。

Enum 支持

一个更复杂的示例,包含枚举

message Ping {
    fixed64 nonce = 1;
}
message Pong {
    fixed64 nonce = 1;
}
message Message {
    oneof kind {
        Ping Ping = 1;
        Pong Pong = 2;
    }
}
#[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),
}

它就这样工作了!

您还可以为枚举变体生成 FromTryFrom 特征。请注意,如果枚举变体具有相同的字段类型,则此功能将不起作用。要使用此功能,请添加 impl_from_trait 属性。

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

From<Ping>From<Pong>以及TryFrom<..>特征将被生成。

与枚举一起使用的另一个属性是rename。它指示宏在属性参数中生成指定case的方法。

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

目前,仅支持蛇形命名。

跳过字段

此宏还支持在struct中跳过字段,这样在序列化时将被忽略,即它们不会被映射到模式中的任何字段。

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

请注意,您只能跳过实现Default特质的字段。

覆盖转换规则

此宏还支持类似serde的属性with,用于具有自定义from_pbto_pb转换实现的模块。

protobuf-convert将使用函数$module::from_pb$module::to_pb代替ProtobufConvert特质为指定的字段。

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

另请参阅

依赖项

~0.8–3MB
~54K SLoC