15个版本
0.1.0 | 2024年5月13日 |
---|---|
0.0.16 | 2024年2月28日 |
0.0.15 | 2023年12月15日 |
0.0.14 | 2023年9月25日 |
0.0.5 | 2021年11月17日 |
#136 in 编码
429,507 每月下载量
在 219 个crate中(194个直接使用) 使用
515KB
11K SLoC
Typify
Typify可以将JSON Schema文档编译成Rust类型。它可以以多种方式使用
-
使用
cargo typify
命令 -
通过宏
import_types!("types.json")
直接在程序中生成Rust类型 -
通过构建器接口在
build.rs
或xtask
中生成Rust类型 -
通过构建器函数生成持久文件,例如在构建API绑定时
如果生成失败、无法编译或总体上表现不佳:请提交一个问题,并包括JSON Schema和Rust输出(如果有)。使用cargo typify
命令从命令行生成代码。如果您能描述您理想中希望看到的输出,那就更有帮助了。
JSON Schema → Rust类型
Typify根据Schema的一些基本属性以几种不同的方式翻译JSON Schema类型
内置类型
整数、浮点数、字符串等。这些都在Rust中有直接表示。唯一需要注意的是如何根据类型属性选择合适的内置类型。例如,JSON Schema可能指定最大值和/或最小值,以指示应使用适当的整数类型。
包含已知格式的字符串模式由相应的Rust类型表示。例如,以下代码{ "type": "string", "format": "uuid" }
被表示为uuid::Uuid
(这需要将uuid
crate作为依赖项包含)。
数组
JSON模式数组可以转换为三种Rust类型之一Vec<T>
、HashSet<T>
和元组,具体取决于模式属性。数组可以具有与固定项目类型列表匹配的固定长度;这可以用Rust元组很好地表示。Vec<T>
和HashSet<T>
之间的区别仅在于模式中uniqueItems
字段设置为false
或true
。
对象
通常,对象转换为Rust结构体。但是,如果模式没有定义属性,并且additionalProperties
模式指定了T
,则Typify会发出一个HashMap<String, T>
,否则发出一个HashMap<String, serde_json::Value>
。
不是required
集合中生成的struct
属性通常表示为一个带有#[serde(default)]
属性的Option<T>
。具有默认值的类型(如Vec<T>>
)的属性将简单地获得#[serde(default)]
属性(因此您不会看到例如Option<Vec<T>>
)。
OneOf
oneOf
构造映射到Rust枚举。Typify将其映射到各种serde枚举类型。
AllOf
“allOf”结构通过合并模式来处理。虽然大部分时间,typify都会尝试保留和共享类型名称,但在合并模式时,它并不能总是做到这一点。您可能会得到在类型间重复的字段;优化这一生成过程是当前工作的一个活跃领域。
AnyOf
anyOf
结构更复杂。它可以接近于一个enum
(oneOf
),但没有任何特定变体可能是特定数据的规范或唯一的。尽管今天我们(不精确地)将这些作为具有可选、展平成员的结构体进行建模,但这仍是代码生成的一个较弱领域。
欢迎和欢迎描述示例模式和期望输出的问题。
Rust -> Schema -> Rust
从Rust类型派生的模式可能包括一个扩展,提供有关原始类型的信息
{
"type": "object",
"properties": { .. },
"x-rust-type": {
"crate": "crate-o-types",
"version": "1.0.0",
"path": "crate_o_types::some_mod::SomeType"
}
}
该扩展包括crate的名称、Cargo风格的版本要求规范以及完整的路径(必须以crate的ident-converted名称开头)。
typify的每种使用模式都允许指定一个crate和版本的列表。在这种情况下,如果用户指定“[email protected]”等,那么typify将使用其SomeType
类型,而不是根据模式生成一个。
使用其他crate的类型
typify的每种使用模式都有一个控制带有x-rust-type
注解的类型使用的方法。默认情况下是忽略它们。推荐的方法是指定您打算使用的每个crate和版本。您还可以为crate提供*
版本(这可能导致不兼容)或定义一个策略以允许使用所有“未知”crate(这可能需要为这些crate添加依赖项)。
对于CLI
$ cargo typify --unknown-crates allow --crate [email protected] ...
对于构建器
let mut settings = typify::TypeSpaceSettings::default();
settings.with_unknown_crates(typify::UnknownPolicy::Allow)
.with_crate("oxnet", typify::CrateVers::Version("1.0.0".parse().unwrap()));
对于宏
typify::import_types!(
schema = "schema.json",
unknown_types = Allow,
crates {
"oxnet" = "1.0.0"
}
)
版本要求
在x-rust-type
扩展中的version
字段遵循Cargo版本要求规范。如果扩展指定了crate的0.1.0
版本,并且用户声明他们正在使用0.1.1
版本,那么将使用该类型;相反,如果扩展指定了0.2.2
版本,而用户只使用0.2.0
版本,则不使用该类型。
盒作者可以选择遵守比semver提供更稳定的规范。如果扩展版本是 >=0.1.0, <1.0.0
,那么盒作者就承诺在所有发布版本上保持给定类型的模式兼容性,直到 1.0.0
。盒作者在version
字段中填写内容时,应确保类型可用性。例如,虽然*
是一个有效值,但只有当相关类型在第一个发布的盒版本中可用,并且在后续版本中从未以不兼容的方式更改时,它才可能有效。
类型参数
x-rust-type
扩展还可以指定类型参数
{
"$defs": {
"Sprocket": {
"type": "object",
"properties": { .. },
"x-rust-type": {
"crate": "util",
"version": "0.1.0",
"path": "util::Sprocket",
"parameters": [
{
"$ref": "#/$defs/Gizmo"
}
]
}
},
"Gizmo": {
"type": "object",
"properties": { .. },
"x-rust-type": {
"crate": "util",
"version": "0.1.0",
"path": "util::Gizmo"
}
}
}
}
在类型生成期间指定的util@0.1.0
盒中,引用#/$defs/Sprocket
的模式将使用(非生成的)类型util::Sprocket<util::Gizmo>
。
parameters
字段是一个模式数组。它们可以是内联模式或引用模式。
将x-rust-type
包含在您的库中
预期值的模式如下
{
"description": "schema for the x-rust-type extension",
"type": "object",
"properties": {
"crate": {
"type": "string",
"pattern": "^[a-zA-Z0-9_-]+$"
},
"version": {
"description": "semver requirements per a Cargo.toml dependencies entry",
"type": "string"
},
"path": {
"type": "string",
"pattern": "^[a-zA-Z0-9_]+(::[a-zA-Z0-9+]+)*$"
},
"parameters": {
"type": "array",
"items": {
"$ref": "#/definitions/Schema"
}
}
},
"required": [
"crate",
"path",
"version"
]
}
version
字段表示您类型的稳定性。例如,如果0.1.0
表示0.1.1
用户可以使用该类型,而0.2.0
用户将无法使用该类型(而是生成它),您可以通过使用Cargo版本要求语法来传达超出semver暗示的未来承诺。例如,>=0.1.0, <1.0.0
表示类型将从版本0.1.0
开始直到1.0.0
保持结构兼容性。
格式化
您可以使用诸如rustfmt-wrapper
和prettyplease
之类的crate来格式化生成的代码。这在进行代码提交或从build.rs
生成代码时尤其有用。
以下示例展示了将 TypeSpace
转换为字符串的不同方法 (typespace
是 typify::TypeSpace
)。
rustfmt
适用于需要与手动编写的代码(如 xtask
或独立代码生成器(如 cargo-typify
))一起提交的代码生成。
rustfmt_wrapper::rustfmt(typespace.to_stream().to_string())?
prettyplease
适用于可能没有安装 rustfmt
的 build.rs
脚本,因此应自包含。
prettyplease::unparse(&syn::parse2::<syn::File>(typespace.to_stream())?)
无格式化
如果没有人会看到代码(这种情况几乎从未发生)。
typespace.to_stream().to_string()
WIP
Typify 正在开发中。影响输出的更改将通过版本号的重大更改来指示。
通常,如果您有一个导致 Typify 失败的 JSON Schema 或生成的类型不是您期望的,请提交问题。
有一些已知领域我们希望改进
复杂的 JSON Schema 类型
JSON schema 可以表达各种类型。其中一些在 Rust 中很容易建模;而另一些则不然。处理这些特殊类型需要做大量工作。用户提供的示例在此方面非常有帮助。
有界数字
有界数字处理得不是很好。例如,考虑以下模式
{
"type": "integer",
"minimum": 1,
"maximum": 6
}
生成的类型不会强制执行这些值约束。
可配置的依赖关系
将 format
设置为 uuid
的字符串模式将生成 uuid::Uuid
类型;类似地,将 format
设置为 date
转换为 chrono::naive::NaiveDate
。对于不想依赖 uuid
或 chrono
的用户,Typify 可选择将这些表示为 String
(或作为其他消费者指定的类型)。
依赖关系
~3.5–5MB
~102K SLoC