Rust结构体验证库superstruct的使用,superstruct提供强类型数据验证和结构体转换功能
Rust结构体验证库superstruct的使用,superstruct提供强类型数据验证和结构体转换功能
SuperStruct是一个用于处理相关结构体变体的库,每个变体共享一些公共字段,并添加自己的唯一字段。
项目展示
SuperStruct被用于以下项目:
sigp/lighthouse
: 以太坊共识客户端
许可证
Apache 2.0
安装
在项目目录中运行以下Cargo命令:
cargo add superstruct
或者在Cargo.toml中添加以下行:
superstruct = "0.10.0"
完整示例代码
use superstruct::superstruct;
// 定义一个基础结构体
#[superstruct(
variants(V1, V2, V3), // 定义变体V1,V2,V3
variant_attributes(derive(Debug, PartialEq)) // 为变体添加属性
)]
#[derive(Debug, PartialEq)]
pub struct Foo {
pub common_field: String,
#[superstruct(only(V1))]
pub v1_field: u32,
#[superstruct(only(V2))]
pub v2_field: String,
#[superstruct(only(V3))]
pub v3_field: Vec<u8>,
}
fn main() {
// 创建V1变体实例
let foo_v1 = Foo::V1 {
common_field: "common".to_string(),
v1_field: 42
};
// 创建V2变体实例
let foo_v2 = Foo::V2 {
common_field: "common".to_string(),
v2_field: "v2 specific".to_string()
};
// 访问公共字段
assert_eq!(foo_v1.common_field(), "common");
assert_eq!(foo_v2.common_field(), "common");
// 访问变体特定字段
match foo_v1 {
Foo::V1(v1) => assert_eq!(v1.v1_field, 42),
_ => panic!("Expected V1 variant")
}
match foo_v2 {
Foo::V2(v2) => assert_eq!(v2.v2_field, "v2 specific"),
_ => panic!("Expected V2 variant")
}
// 转换示例
let foo_v1_as_enum: Foo = foo_v1.into();
match foo_v1_as_enum {
Foo::V1(_) => println!("It's a V1"),
_ => panic!("Should be V1")
}
}
代码说明
- 使用
#[superstruct]
宏定义了一个基础结构体Foo
和它的三个变体(V1,V2,V3) - 每个变体都包含一个公共字段
common_field
- 每个变体有自己的特定字段(v1_field, v2_field, v3_field)
- 示例展示了如何创建不同变体的实例
- 演示了如何访问公共字段和变体特定字段
- 展示了如何将具体变体转换为枚举类型进行模式匹配
这个库特别适合处理协议版本演进或API不同版本的数据结构,可以方便地维护共享字段同时处理变体特定的字段。
完整示例Demo
以下是一个更完整的示例,展示superstruct在实际应用中的使用方式:
use superstruct::superstruct;
use serde::{Serialize, Deserialize};
// 定义一个API响应结构体,支持多个版本
#[superstruct(
variants(V1, V2),
variant_attributes(derive(Debug, Clone, PartialEq, Serialize, Deserialize))
)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApiResponse {
// 公共字段
pub status: u16,
pub message: String,
// V1版本特有字段
#[superstruct(only(V1))]
pub timestamp: String,
// V2版本特有字段
#[superstruct(only(V2))]
pub data: Vec<u8>,
#[superstruct(only(V2))]
pub checksum: String,
}
fn handle_response(response: ApiResponse) {
println!("Response status: {}", response.status());
println!("Message: {}", response.message());
match response {
ApiResponse::V1(v1) => {
println!("V1 response - Timestamp: {}", v1.timestamp);
}
ApiResponse::V2(v2) => {
println!("V2 response - Data length: {}, Checksum: {}",
v2.data.len(), v2.checksum);
}
}
}
fn main() {
// 创建V1响应
let v1_response = ApiResponse::V1 {
status: 200,
message: "OK".to_string(),
timestamp: "2023-01-01T00:00:00Z".to_string(),
};
// 创建V2响应
let v2_response = ApiResponse::V2 {
status: 200,
message: "OK".to_string(),
data: vec![1, 2, 3, 4, 5],
checksum: "abc123".to_string(),
};
// 处理不同版本的响应
handle_response(v1_response.into());
handle_response(v2_response.into());
// 序列化/反序列化示例
let json_v1 = serde_json::to_string(&v1_response).unwrap();
println!("Serialized V1: {}", json_v1);
let json_v2 = serde_json::to_string(&v2_response).unwrap();
println!("Serialized V2: {}", json_v2);
}
这个完整示例展示了:
- 定义支持多版本API响应的结构体
- 不同版本有公共字段和特有字段
- 如何处理不同版本的响应
- 结合serde实现序列化/反序列化
- 类型安全地访问不同版本的字段
1 回复
Rust结构体验证库superstruct的使用指南
介绍
superstruct是一个Rust库,专注于提供强类型的数据验证和结构体转换功能。它允许你定义数据结构的预期形状,然后验证输入数据是否符合该结构,并在验证过程中自动转换为强类型结构体。
主要特性:
- 声明式验证规则
- 自动错误收集
- 支持嵌套结构验证
- 类型安全的转换
- 友好的错误信息
安装
在Cargo.toml中添加依赖:
[dependencies]
superstruct = "0.6"
基本使用方法
1. 定义结构体
use superstruct::superstruct;
#[superstruct]
struct User {
id: String,
name: String,
#[superstruct(only(V2))]
email: String,
age: u8,
}
2. 验证和转换
let data = serde_json::json!({
"id": "123",
"name": "Alice",
"age": 25
});
let user = User::from_value(data).unwrap();
println!("User: {:?}", user);
进阶用法
版本化结构体
#[superstruct]
struct Message {
text: String,
#[superstruct(only(V2))]
timestamp: i64,
#[superstruct(only(V1))]
is_read: bool,
}
// 使用V1版本
let v1_data = serde_json::json!({
"text": "Hello",
"is_read": true
});
let msg_v1 = Message::from_value(v1_data).unwrap();
// 使用V2版本
let v2_data = serde_json::json!({
"text": "Hello",
"timestamp": 1672531200
});
let msg_v2 = Message::from_value(v2_data).unwrap();
自定义验证
#[superstruct]
struct Product {
id: String,
#[superstruct(validator = "validate_price")]
price: f64,
}
fn validate_price(price: &f64) -> Result<(), String> {
if *price > 0.0 {
Ok(())
} else {
Err("Price must be positive".to_string())
}
}
错误处理
let invalid_data = serde_json::json!({
"id": "123",
"price": -10.0
});
match Product::from_value(invalid_data) {
Ok(_) => println!("Valid product"),
Err(e) => println!("Validation errors: {:?}", e),
}
实际示例
API请求验证
#[superstruct]
struct ApiRequest {
#[superstruct(validator = "validate_api_key")]
api_key: String,
action: String,
#[superstruct(only(PremiumRequest))]
premium_feature: bool,
}
fn validate_api_key(key: &str) -> Result<(), String> {
if key.starts_with("sk_") && key.len() == 32 {
Ok(())
} else {
Err("Invalid API key format".to_string())
}
}
// 使用
let request_data = serde_json::json!({
"api_key": "sk_123456789012345678901234567890",
"action": "create"
});
let request = ApiRequest::from_value(request_data).unwrap();
配置验证
#[superstruct]
struct AppConfig {
#[superstruct(validator = "validate_port")]
port: u16,
db_url: String,
#[superstruct(default = "false")]
debug_mode: bool,
}
fn validate_port(port: &u16) -> Result<(), String> {
if *port > 1024 {
Ok(())
} else {
Err("Port must be greater than 1024".to_string())
}
}
// 使用默认值
let config_data = serde_json::json!({
"port": 8080,
"db_url": "postgres://user:pass@localhost/db"
});
let config = AppConfig::from_value(config_data).unwrap();
assert_eq!(config.debug_mode(), false);
完整示例demo
下面是一个完整的示例,展示了如何使用superstruct进行用户注册数据的验证:
use superstruct::superstruct;
use serde_json;
// 定义用户结构体
#[superstruct]
struct UserRegistration {
#[superstruct(validator = "validate_username")]
username: String,
#[superstruct(validator = "validate_email")]
email: String,
#[superstruct(validator = "validate_password")]
password: String,
#[superstruct(default = "false")]
is_verified: bool,
}
// 用户名验证函数
fn validate_username(username: &str) -> Result<(), String> {
if username.len() >= 3 && username.len() <= 20 {
Ok(())
} else {
Err("Username must be between 3 and 20 characters".to_string())
}
}
// 邮箱验证函数
fn validate_email(email: &str) -> Result<(), String> {
if email.contains('@') && email.contains('.') {
Ok(())
} else {
Err("Invalid email format".to_string())
}
}
// 密码验证函数
fn validate_password(password: &str) -> Result<(), String> {
if password.len() >= 8 {
Ok(())
} else {
Err("Password must be at least 8 characters".to_string())
}
}
fn main() {
// 有效数据示例
let valid_data = serde_json::json!({
"username": "rustfan",
"email": "user@example.com",
"password": "secure123"
});
match UserRegistration::from_value(valid_data) {
Ok(user) => {
println!("Registration successful!");
println!("Username: {}", user.username());
println!("Email: {}", user.email());
println!("Verified: {}", user.is_verified());
}
Err(e) => println!("Validation errors: {:?}", e),
}
// 无效数据示例
let invalid_data = serde_json::json!({
"username": "a", // 太短
"email": "invalid", // 格式错误
"password": "short" // 太短
});
match UserRegistration::from_value(invalid_data) {
Ok(_) => println!("This should not happen!"),
Err(e) => {
println!("Registration failed with errors:");
for error in e {
println!("- {}", error);
}
}
}
}
这个完整示例展示了:
- 定义带有验证规则的结构体
- 实现自定义验证函数
- 使用默认值
- 处理验证成功和失败的情况
- 输出友好的错误信息
superstruct通过提供类型安全的验证和转换,可以帮助你在Rust应用中构建更健壮的数据处理流程,特别是在处理外部输入(如API请求、配置文件等)时非常有用。