3个版本
0.1.2 | 2023年11月1日 |
---|---|
0.1.1 | 2023年10月31日 |
0.1.0 | 2023年10月31日 |
#479 in Rust模式
16KB
118 行
Proto Mapper
自定义模型和protobuf生成的代码之间的宏实现库
注意
这个库是对protobuf-convert库的(几乎)完全重写。重写目的是为了适应我们项目的特定需求。主要概念和思想保持不变,因此,荣誉归功于protobuf-convert
库的原始作者。
变化了什么
这个库
- 将宏的主要名称更改为
ProtoMap
- 改变了宏的使用方式和与外部特质的接口
- 避免在客户端模块中重新实现ProtoMap特质
- 重构为不同的crate
- 包含对边缘情况的过度测试
- 引入ProtoScalar类型
- 引入用于protobuf标量类型的ProtoScalarMap特质
- 自动处理枚举protobuf生成代码
- 通过扫描应用结构的类型来处理选项值,并选择不同的实现路径
- 支持prost
安装
首先,在Cargo.toml
中添加依赖项
proto-mapper = {version = "0.1.2", features = ["protobuf"] }
或者
proto-mapper = {version = "0.1.2", features = ["prost"] }
注意:功能prost
或protobuf
是相互排斥且必需的。根据您使用的目标生成的代码proto框架,使用其中一个
用法
可以在这里找到展示该库使用的概念证明。请注意,PoC仍在进行中。
映射标量值和枚举
给定protobuf枚举和消息
syntax = "proto3";
enum EntityStatus {
STATUS_A = 0;
STATUS_B = 1;
STATUS_C = 2;
}
message ScalarEntity {
uint32 uint32_f = 1;
int32 int32_f= 2;
bool bool_f = 4;
string string_f = 5;
int64 int64_f = 6;
uint64 uint64_f = 7;
bytes bytes_f = 8;
float float_f = 9;
double double_f = 10;
EntityStatus status = 11;
}
使用prost
或rust-protobuf
库生成代码后,您可以将您的自定义模型映射到生成的结构体,如下所示
#[derive(Debug, Clone, Copy, Default, PartialEq, ProtoMap)]
#[proto_map(
source = "proto::EntityStatus",
enumeration,
)]
enum EntityStatus {
#[default]
StatusA,
StatusB,
StatusC,
}
#[derive(Debug, ProtoMap, Default)]
#[proto_map(source = "proto::ScalarEntity")]
struct ScalarEntity {
pub uint32_f: u32,
pub int32_f: i32,
pub bool_f: bool,
pub string_f: String,
pub bytes_f: Vec<u8>,
pub int64_f: i64,
pub uint64_f: u64,
pub float_f: f32,
pub double_f: f64,
#[proto_map(enumeration)]
pub status: EntityStatus,
}
然后您可以将您定义的结构体和生成的代码之间进行转换,如下所示
let e = ScalarEntity::default();
let p = e.to_proto();
您还可以将proto实例转换为您的自定义结构体。
let p = proto::ScalarEntity::default();
let e = ScalarEntity::from_proto(p)?;
请注意,枚举的映射代码需要在Rust枚举上使用#[proto_map(..., enumeration)]
属性,还需要在ScalarEntity
内部的字段上进行标记。
映射可选标量值和枚举
给定相同的 proto 文件。开箱即用即可映射到可选值
也就是说
#[derive(Debug, ProtoMap, PartialEq, Default)]
#[proto_map(source = "proto::prost::ScalarEntity")]
struct ScalarEntityOptions {
pub uint32_f: Option<u32>,
pub int32_f: Option<i32>,
pub bool_f: Option<bool>,
pub string_f: Option<String>,
pub bytes_f: Option<Vec<u8>>,
pub int64_f: Option<i64>,
pub uint64_f: Option<u64>,
pub float_f: Option<f32>,
pub double_f: Option<f64>,
#[proto_map(enumeration)]
pub status: Option<EntityStatus>,
}
宏扫描自定义结构体中注解的类型,并选择不同的实现路径以进行转换代码。
映射非标量值
给定 proto 文件
syntax = "proto3";
// ... definitions of ScalarEntity
message NestedEntity {
ScalarEntity first = 1;
ScalarEntity second = 2;
}
您可以如下映射非标量值
#[derive(Debug, ProtoMap, PartialEq)]
#[proto_map(source = "proto::NestedEntity")]
struct NestedEntity {
pub first: ScalarEntity,
pub second: Option<ScalarEntity>,
}
将非标量 oneof
字段映射到 Rust 枚举
您可以将顶级 oneof
protobuf 字段映射如下
给定 proto 文件
syntax = "proto3";
// ... definitions of ScalarEntity
message HierarchyEntity {
oneof data {
ScalarEntity first_entity = 1;
NestedEntity second_entity = 2;
}
}
然后自定义结构体的一个实现可能如下
#[derive(Debug, ProtoMap, PartialEq)]
#[proto_map(
source = "proto::HierarchyEntity",
one_of(field = "data"),
rename_variants = "snake_case"
)]
enum HierarchyEntity {
FirstEntity(ScalarEntity),
SecondEntity(NestedEntity),
}
请注意,rename_variants
属性可以取两个值 snake_case
和 STREAMING_SNAKE_CASE
,具体取决于目标生成的结构体。
自定义映射标量值
请参阅测试示例 针对 prost 和 针对 rust-protobuf
prost
和 rust-protobuf
使用之间的差异
待办事项
它的工作原理
内部,宏在标量和非标量类型之间进行区分。
以下列出标量类型,以及 rust-protobuf
和 prost
自动生成的代码映射的 protobuf 类型。
Protobuf 类型 | Rust 类型 |
---|---|
double |
f64 |
float |
f32 |
int32 |
i32 |
int64 |
i64 |
uint32 |
u32 |
uint64 |
u64 |
sint32 |
i32 |
sint64 |
i64 |
fixed32 |
u32 |
fixed64 |
u64 |
sfixed32 |
i32 |
sfixed64 |
i64 |
bool |
bool |
string |
String |
bytes |
Vec<u8> |
(表来自 prost 项目 README.md) |
所有其他 Rust 类型都视为非标量。但有一个例外是 protobuf enum
类型,需要使用元属性 #[proto_map(enumeration)]
标记。
库根据结构体类型自动实现两个特质。
对于标量类型
pub trait ProtoMapScalar<P: ProtoScalar>: Sized {
/// Converts a reference of [`Self`] to a [`ProtoScalar`]
fn to_scalar(&self) -> P;
/// Consumes a [`ProtoScalar`] and returns a [`Self`] or error in the conversion failed
fn from_scalar(proto: P) -> Result<Self, anyhow::Error>;
}
对于非标量类型
pub trait ProtoMap
where
Self: Sized,
{
type ProtoStruct;
/// Converts a reference of [`Self`] struct to proto [`Self::ProtoStruct`]
fn to_proto(&self) -> Self::ProtoStruct;
/// Consumes a proto [`Self::ProtoStruct`] and returns a [`Self`] struct or error in the conversion failed
fn from_proto(proto: Self::ProtoStruct) -> Result<Self, anyhow::Error>;
}
请注意,enum
类型在 rust-protobuf
中被视为非标量,但在 prost
中被视为标量(i32 值)。
库还为所有 proto 标量类型提供了一个名为 ProtoScalar
的特质实现。
要了解宏的实现,请参阅 prost 手动测试 或 rust-protobuf 手动测试,这些测试用作创建实现的指南。
局限性
待办事项
相关项目
资源
依赖关系
~0.8–3.5MB
~56K SLoC