9个重大版本更新
0.10.0 | 2024年6月24日 |
---|---|
0.8.0 | 2024年4月2日 |
0.7.0 | 2024年3月7日 |
0.6.0 | 2023年11月14日 |
#78 in 过程宏
82KB
2K SLoC
Rust的Parquet代码生成
本项目提供使用Rust实现Parquet文件的生成Rust代码的工具。它包括一个代码生成crate (parquetry-gen
) 和一个由生成的代码所需的小型运行时库 (parquetry
)。
请注意,该软件不是“开源”的,但源代码可供个人、非营利组织和企业使用和修改(有关详细信息,请参阅下面的许可协议部分)。
目录
示例
给定如下模式
message user {
required int64 id (integer(64, false));
required int64 ts (timestamp(millis, true));
optional int32 status;
optional group user_info {
required byte_array screen_name (string);
optional group user_name_info {
required byte_array name (string);
optional group user_profile_info {
required int64 created_at (timestamp(millis, true));
required byte_array location (string);
required byte_array description (string);
optional byte_array url (string);
required int32 followers_count;
required int32 friends_count;
required int32 favourites_count;
required int32 statuses_count;
optional group withheld_in_countries (list) {
repeated group list {
required byte_array element (string);
}
}
}
}
}
}
代码生成器将生成以下Rust结构体
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct User {
pub id: u64,
#[serde(with = "chrono::serde::ts_milliseconds")]
pub ts: chrono::DateTime<chrono::Utc>,
pub status: Option<i32>,
pub user_info: Option<UserInfo>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct UserInfo {
pub screen_name: String,
pub user_name_info: Option<UserNameInfo>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct UserNameInfo {
pub name: String,
pub user_profile_info: Option<UserProfileInfo>,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct UserProfileInfo {
#[serde(with = "chrono::serde::ts_milliseconds")]
pub created_at: chrono::DateTime<chrono::Utc>,
pub location: String,
pub description: String,
pub url: Option<String>,
pub followers_count: i32,
pub friends_count: i32,
pub favourites_count: i32,
pub statuses_count: i32,
pub withheld_in_countries: Option<Vec<String>>,
}
它还将为User
生成一个parquetry::Schema
trait的实例,其中包含将值读写到Parquet文件的代码。
依赖项
所有使用都需要使用parquetry
、parquet
、chrono
和lazy_static
crate作为运行时依赖项。
如果配置中启用了serde_support
标志(默认情况下已启用),您还需要依赖serde
,并启用derive
功能。
如果配置中启用了 tests
标志(也是默认设置),您需要将 bincode
(版本 2.0.0-rc.x
,并启用 serde
功能)、tempdir
和 quickcheck
添加到您的 dev-dependencies
中。
使用方法
example
目录提供了一个相当简化的示例,生成的代码也被检查在那里。在大多数情况下,以下类似的 build.rs
就足够了:
use std::{fs::File, io::Write};
fn main() -> Result<(), parquetry_gen::error::Error> {
for schema in parquetry_gen::ParsedFileSchema::open_dir(
"src/schemas/",
Default::default(),
Some(".parquet.txt"),
)? {
println!("cargo:rerun-if-changed={}", schema.absolute_path_str()?);
let mut output = File::create(format!("src/{}.rs", schema.name))?;
write!(output, "{}", schema.code()?)?;
}
Ok(())
}
默认情况下,生成的代码使用 prettyplease
格式化,并带有注释,表示它不应该由 Rustfmt 格式化,但如果您希望使用 Rustfmt,可以在配置中将 format
设置为 false。
测试
默认配置将生成使用 QuickCheck 生成任意值并确认它们正确序列化和反序列化的测试代码。
当前测试代码生成不支持某些类型。具体来说,不支持浮点数的序列、固定长度的字节数组和时间戳。如果您的模式包含这些类型中的任何一种,生成的测试代码将无法编译,您必须在配置中禁用 tests
标志(如果您愿意,也可以提交一个问题)。
生成的测试代码不会为浮点类型生成 NaN
值。如果您想确认您的系统正确处理这些值,您将需要手动进行。
默认情况下,为您的类型生成的任意值可能非常大。如果您的测试速度太慢,您可能需要将 QUICKCHECK_GENERATOR_SIZE
环境变量设置为较小的值(例如 10
或 20
)。
状态和范围
这些工具支持具有大多数物理和逻辑类型的模式,以及列表、可选字段和结构的任意嵌套。
我可能在某个时候添加的一些缺少的功能
- 8 位和 16 位逻辑整数类型(很简单,我只是还没有需要它们)
DATE
、TIME
、INTERVAL
和UUID
(与之前相同)DECIMAL
(至少对于INT32
和INT64
表示形式)ENUM
(在这个上下文中实际上并不很有用,因为模式并没有枚举变体?)- 映射(需要更多工作,但可能值得拥有)
可能永远不会支持的功能
此项目与 parquet_derive
在几个方面有所不同
- 两者都生成读取和写入代码,但此项目从模式生成 Rust 结构体,而不是相反。
- 此项目不使用
parquet::record::RecordWriter
(这似乎并不那么有用,而且我希望有更多的灵活性)。 - 此项目支持嵌套结构。
通常这两个项目有不同的用例,如果您只想将一些Rust值存储在Parquet中,我建议选择 parquet_derive
。
警告
名称冲突
目前还没有对与Rust关键字、标准库中的名称等冲突的字段名称进行特殊处理。组名称也应在该模式内唯一。代码生成器还会生成可能理论上与用户生成的代码冲突的非公开struct。
在这些大多数情况下,问题应该立即明显,因为生成的代码通常会无法编译。检查这些冲突并提供更好的错误,或者允许用户有更多命名控制以避免这些问题并不困难,但这并不是我的优先事项。
构造函数
生成的代码包括每个struct的fn new
构造函数,这些构造函数将截断任何 DateTime<Utc>
的精度到列表示支持的子秒位数。这些构造函数还会检查任何字符串参数是否包含空字节,并在存在时返回错误。
如果您不希望这两种行为中的任何一种,可以手动构造struct,因为所有字段始终是公开的。
Serde实例
默认情况下,生成的代码将包括用于序列化的派生Serde实例。这些实例将使用模式中指定的时间单位(毫秒或微秒)为类型为DateTime<Utc>
或Option<DateTime<Utc>>
的字段使用,但类型为Vec<DateTime<Utc>>
的字段将使用默认的Serde序列化编码。
这没有特殊的原因,只是因为chrono::serde
只提供了例如ts_milliseconds
和ts_milliseconds_option
函数,并且运行时库可以轻松提供自己的ts_milliseconds_vec
,这些函数将在这些情况下使用。
性能
我没有尝试优化生成的代码,除非性能成为我的用例的问题。
许可协议
本软件根据反资本主义软件许可协议(版本1.4)发布。
依赖项
~17–24MB
~505K SLoC