7 个版本 (破坏性更新)
0.10.0 | 2024年6月24日 |
---|---|
0.9.0 | 2024年5月17日 |
0.8.0 | 2024年4月2日 |
0.7.0 | 2024年3月7日 |
0.4.0 | 2023年10月4日 |
#1762 in 解析器实现
每月477次下载
36KB
322 行
为 Rust 编写 Parquet 代码
本项目提供生成 Rust 代码的工具,用于使用 Parquet 文件,该代码使用了 Arrow 的 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关键字、标准库中的名称等冲突的字段名称进行特殊处理。组名称也应在该模式内保持唯一。代码生成器还会生成非公共结构体,其名称理论上可能与其他用户生成的代码冲突。
在这些大多数情况下,问题应该立即显而易见,因为生成的代码通常根本无法编译。检查这些冲突并提供更好的错误,或者允许用户更多地控制命名以避免这些问题并不困难,但这对我来说还不是优先事项。
构造函数
生成的代码包括每个结构的fn new
构造函数,这些构造函数将截断任何DateTime<Utc>
的精度到列表示支持的子秒数字。这些构造函数还将检查任何字符串参数是否包含空字节,并在发现空字节时返回错误。
如果你不希望有任何这些行为,你可以手动构造结构体,因为所有字段始终是公开的。
Serde实例
默认情况下,生成的代码将包括用于序列化的派生Serde实例。这些实例将使用模式指定的时单位(毫秒或微秒)来处理类型为DateTime<Utc>
或Option<DateTime<Utc>>
的字段,但类型为Vec<DateTime<Utc>>
的字段将使用默认的Serde序列化编码来处理DateTime<Utc>
。
这并非没有原因,只是因为chrono::serde
仅提供了例如ts_milliseconds
和ts_milliseconds_option
函数,并且运行时库可以轻松提供自己的ts_milliseconds_vec
,这些函数将用于这些情况。
性能
我没有尝试优化生成的代码,可能直到性能成为我的用例问题之前都不会。
许可证
此软件根据Anti-Capitalist Software License(v. 1.4)发布。
依赖项
~48MB
~1M SLoC