共计 2475 个字符,预计需要花费 7 分钟才能阅读完成。
内容目录
Rust 编译并使用 Protobuf
必要的依赖库
prost
: https://github.com/tokio-rs/prost、https://docs.rs/prost/latest/prost/prost-types
: https://docs.rs/prost/latest/prost/prost-build
: https://docs.rs/prost-build/latest/prost_build/
安装
Cargo
cargo add prost
cargo add prost-types
cargo add --build prost-build
依赖文件
[dependencies]
prost = "0.13.4"
prost-types = "0.13.4"
[build-dependencies]
prost-build = "0.13.4"
编写 proto 文件
syntax = "proto3";
package cmd_type;
// 传输数据的指令类型
enum CmdType {
///// 通用
HEARTBEAT = 0; // 心跳检测
AUTH = 1; // 认证
AUTH_OK = 2; // 认证通过
AUTH_ERR = 3; // 认证失败
CONNECT = 4; // 连接
DISCONNECT = 5; // 断开连接
TRANSFER = 6; // 数据传输
OPEN_SERVER = 7; // 开启代理端口
CLOSE_SERVER = 8; // 关闭代理端口
}
syntax = "proto3";
package meta_data;
import "google/protobuf/timestamp.proto"; // 导入时间戳消息
// 传输数据的消息定义
message TransferMessageMetaData {
google.protobuf.Timestamp timestamp = 1; // 时间戳
map<string,string> metaData = 2; // 元数据
}
编写 build 脚本
在 src 同级下创建一个build.rs
文件,代码如下:
use std::fs;
fn main() {
let out_dir = "src/core";
// 创建输出目录
fs::create_dir_all(out_dir).unwrap();
prost_build::Config::new()
.out_dir(out_dir) // 指定输出目录
.compile_protos(
&["proto/cmd_type.proto", "proto/meta_data.proto"], // .proto 文件
&["proto/"], // 包含目录
)
.expect("Failed to compile .proto files");
// 创建顶级模块
let mod_file = format!("{}/mod.rs", out_dir);
let mod_content = ["cmd_type.rs", "meta_data.rs"]
.iter()
.map(|file| format!("pub mod {};", file.trim_end_matches(".rs")))
.collect::<Vec<_>>()
.join("\n");
std::fs::write(&mod_file, mod_content).unwrap();
}
build.rs
通常用于在 Rust 项目编译前执行一些构建相关的任务:
-
设置输出目录:
let out_dir = "src/core";
指定了生成的 Rust 文件将被输出到
src/core
目录中。 -
创建输出目录:
fs::create_dir_all(out_dir).unwrap();
使用
create_dir_all
函数创建输出目录。如果该目录已经存在,函数不会报错。如果创建失败,会触发unwrap()
导致程序崩溃并输出错误信息。 -
编译 Proto 文件:
prost_build::Config::new() .out_dir(out_dir) // 指定输出目录 .compile_protos( &["proto/cmd_type.proto", "proto/meta_data.proto"], // .proto 文件 &["proto/"], // 包含目录 ) .expect("Failed to compile .proto files");
在这里,使用
prost_build
库配置并编译 Protocol Buffers 文件:out_dir
: 指定生成的 Rust 文件应放置的目录。compile_protos
: 第一个参数包含待编译的.proto
文件,第二个参数指定.proto
文件的包含目录。- 如果编译失败,
expect
方法将输出错误消息并导致程序崩溃。
-
创建顶级模块文件:
let mod_file = format!("{}/mod.rs", out_dir);
创建一个
mod.rs
文件的路径,这是 Rust 中用于声明模块的文件。 -
生成模块内容:
let mod_content = ["cmd_type.rs", "meta_data.rs"] .iter() .map(|file| format!("pub mod {};", file.trim_end_matches(".rs"))) .collect::
>() .join("\n"); - 定义一个数组
["cmd_type.rs", "meta_data.rs"]
,包含将生成的 Rust 文件。 - 使用
iter()
遍历数组,并对每个文件生成模块声明,例如pub mod cmd_type;
。 trim_end_matches(".rs")
用于去掉文件名的.rs
后缀,确保生成的模块声明是有效的。collect::<Vec<_>>()
将迭代结果收集到向量中。join("\n")
将向量中的模块声明连接成单一字符串,每个模块声明之间使用换行符分隔。
- 定义一个数组
-
写入模块文件:
std::fs::write(&mod_file, mod_content).unwrap();
将生成的模块内容写入到
mod.rs
文件中。如果写入过程中出现错误,unwrap()
将导致程序崩溃并显示错误信息。编译
使用
cargo build
命令编译。
正文完