47次发布

0.9.4 2023年12月14日
0.9.3 2023年8月28日
0.9.1 2022年10月27日
0.9.0-beta.12022年3月23日
0.3.4 2019年7月25日

#90 in 解析实现

Download history 3485/week @ 2024-04-23 2793/week @ 2024-04-30 2766/week @ 2024-05-07 2668/week @ 2024-05-14 3186/week @ 2024-05-21 3706/week @ 2024-05-28 3469/week @ 2024-06-04 3318/week @ 2024-06-11 2918/week @ 2024-06-18 3228/week @ 2024-06-25 3259/week @ 2024-07-02 3530/week @ 2024-07-09 2712/week @ 2024-07-16 3272/week @ 2024-07-23 2921/week @ 2024-07-30 3508/week @ 2024-08-06

13,080 每月下载量
cddlconv中使用

MIT 许可证

740KB
19K SLoC

cddl-rs

crates.io docs.rs Publish packages Build and Test Active Development

此crate最初作为了解Rust和解析的一般方法的个人学习练习而开发。可能还有更高效和更稳定的CDDL解析库。虽然有一些此crate在生产中使用的示例,但在使用此crate作为此类用途之前应谨慎考虑。

CDDL(简明数据定义语言)的Rust实现。CDDL是IETF标准,它“提出了一种表示CBOR和JSON数据结构的记法约定。”截至2019年6月12日,它作为RFC 8610(提议标准)发布于https://tools.ietf.org/html/rfc8610

此crate包含CDDL的手写解析器和词法分析器,其开发受到了Thorsten Ball的书籍"用Go编写解释器"中概述的技术的极大启发。AST已构建以紧密匹配规范中ABNF语法的规则附录B。所有CDDL都必须根据规范使用UTF-8进行编码。

此crate支持验证CBOR和JSON数据结构。最低支持的Rust版本(MSRV)是1.67.0。

此外,此仓库还包括一个基本的语言服务器实现和Visual Studio Code扩展,用于编辑CDDL。实现由此crate中包含的编译WebAssembly目标支持。

目标

  • 将CDDL文档解析到AST
  • 验证CDDL文档与RFC 8610的一致性
  • 验证CBOR数据结构
  • 验证JSON文档
  • 从符合CDDL的文档生成示例JSON
  • 尽可能接近零复制
  • 为浏览器和Node.js编译WebAssembly目标
  • 无std 支持(仅词法和解析)
  • 语言服务器实现和Visual Studio Code扩展

非目标

  • 性能(如果这个crate(软件包)获得足够的关注,进行更正式的性能分析或探索使用像nom这样的解析器组合框架可能是谨慎的)
  • 支持CBOR诊断表示法
  • I-JSON兼容性

为什么选择Rust?

Rust是一种围绕安全性设计的系统编程语言,非常适合资源受限的系统。CDDL和CBOR都是针对小型代码和消息大小以及受限节点设计的,Rust也为此类场景进行了设计。

命令行界面(CLI)

适用于各种平台。该工具支持解析CDDL文件以验证是否符合RFC 8610。它还可以用于验证JSON文档和CBOR二进制文件是否符合CDDL文档。有关JSON和CBOR验证实现的详细信息,请参阅下面的部分。

安装

GitHub发行版

可以从GitHub 发行版下载Linux、macOS和Windows的二进制文件。

Cargo

cargo install cddl

Docker

docker pull ghcr.io/anweiss/cddl-cli:latest

CLI使用

可以通过执行help子命令来查看使用此工具的说明。

cddl help

如果使用Docker

<version>替换为适当的发行版标签。在执行命令时,需要使用--volume参数将CDDL文档挂载到容器中。JSON或CBOR文件可以包含在卷挂载中,或者通过STDIN传递给命令。

docker run -it --rm -v $PWD:/cddl -w /cddl ghcr.io/anweiss/cddl-cli:<version> help

您可以使用它来验证JSON文档和/或CBOR二进制文件

cddl validate [OPTIONS] --cddl <CDDL> <--stdin|--json <JSON>...|--cbor <CBOR>...>

它还支持验证STDIN中的文件(如果它检测到输入为有效的UTF-8,则将尝试将输入作为JSON进行验证,否则将其视为CBOR)

cat reputon.json | cddl validate --cddl reputon.cddl --stdin
cat reputon.cbor | cddl validate --cddl reputon.cddl --stdin

或使用Docker

docker run -i --rm -v $PWD:/data -w /data ghcr.io/anweiss/cddl-cli:0.9.3 validate --cddl reputon.cddl --stdin < reputon.json

网站

您还可以在https://cddl.anweiss.tech找到简单的RFC 8610兼容性工具。此相同的代码库已编译为通过WebAssembly在浏览器中使用。

Visual Studio Code扩展

一个用于在Visual Studio Code中编辑CDDL文档的扩展已发布到市场这里。您可以在README中找到更多信息。

支持的功能

  • 映射
    • 结构体
    • 裁剪
  • 数组
  • 选择
  • 范围
  • 枚举(从组构建选择)
  • 根类型
  • 出现次数
  • 预定义类型
  • 标签
  • 展开
  • 控制
  • 插座/插头
  • 泛型
  • 操作符优先级
  • 注释
  • 数值int/uint
  • 数值十六进制浮点数
  • 具有指数的数值
  • 无前缀的字节字符串
  • 有前缀的字节字符串

使用方法

只需将依赖关系添加到Cargo.toml

[dependencies]
cddl = "0.9.3"

JSON和CBOR验证都需要std

功能标志

包含了一些便利功能,以使抽象语法树(AST)更加简洁,并启用额外的功能。您可以使用default-features = false构建no_std版本,并选择性地启用以下任何功能。

--feature ast-span

Span类型添加到AST中,以跟踪词法分析和解析器的位置。默认启用。

--feature ast-comments

在AST中包含注释字符串。默认启用。

--feature ast-parent

添加ParentVisitor实现,以便可以使用父指针遍历AST。默认启用。

--feature json

启用JSON验证。默认启用。

--feature cbor

启用CBOR验证。默认启用。

--功能:额外的控件

启用对在RFC 9165中定义的额外控件操作符的验证支持。默认启用。

解析 CDDL

use cddl::parser::cddl_from_str;

let input = r#"myrule = int"#;
assert!(cddl_from_str(input, true).is_ok())

验证 JSON

use cddl::validate_json_from_str;

let cddl = r#"person = {
  name: tstr,
  age: uint,
  address: tstr,
}"#;

let json = r#"{
  "name": "John",
  "age": 50,
  "address": "1234 Lakeshore Dr"
}"#;

assert!(validate_json_from_str(cddl, json).is_ok())

此 crate 使用 Serde 框架,特别是 serde_json crate,用于解析和验证 JSON。选择 Serde 是因为它在生态系统中的成熟度以及它通过 ciborium crate 支持序列化和反序列化 CBOR。

如标准附录 E(附录 E)所述,只能使用 CBOR 数据模型子集的 JSON 进行验证。以下为为了简洁而包含的有限预置:

any = #

uint = #0
nint = #1
int = uint / nint

tstr = #3
text = tstr

number = int / float

float16 = #7.25
float32 = #7.26
float64 = #7.27
float16-32 = float16 / float32
float32-64 = float32 / float64
float = float16-32 / float64

false = #7.20
true = #7.21
bool = false / true
nil = #7.22
null = nil

此外,以下标准预置中的数据类型可用于验证 JSON 字符串和数字

tdate = #6.0(tstr)
uri = #6.32(tstr)
b64url = #6.33(tstr)
time = #6.1(number)

CDDL 数据结构定义中定义的第一个非组规则确定了根类型,随后用于验证顶层 JSON 数据类型。

支持的 JSON 验证功能

以下 CDDL 类型和支持的功能可用于此 crate 验证 JSON

CDDL JSON
结构体 对象
数组 数组1
文本/tstr 字符串
uri 字符串(有效的 RFC3986 URI)
tdate 字符串(有效的 RFC3339 日期/时间)
b64url 字符串(base64url 编码)
时间 数字(有效的 UNIX 时间戳整数,以秒为单位)
数字/int/float 数字2
bool / true / false 布尔值
null/nil null
任何 任何有效的 JSON
字节字符串 尚未实现
unwrap (~) 与映射、数组或标签中的 unwrapped 类型匹配的任何 JSON

CDDL 组、泛型、插座/插头和组到选择枚举都可以用于验证 JSON。

由于 JSON 对象仅支持键的类型为 JSON 字符串,因此当验证 JSON 时,CDDL 结构中定义的成员键必须使用冒号语法(mykey: tstr"mykey": tstr)或双箭头语法(如果成员键是文本字符串值("mykey" => tstr)或裸词,它解析为字符串数据类型(texttstr)或另一个文本字符串值(* tstr => any))。

可以使用出现指示符来验证 JSON 对象中的键/值对以及 JSON 数组中的元素数量;具体取决于在 CDDL 数据定义中如何定义指示符。

以下是支持的控件操作符表

控件操作符 支持
.pcre ✔️3
.regex ✔️3.pcre 的别称)
.size ✔️
.bits 在验证 JSON 时忽略
.cbor 在验证 JSON 时忽略
.cborseq 在验证 JSON 时忽略
.within ✔️
.and ✔️
.lt ✔️
.le ✔️
.gt ✔️
.ge ✔️
.eq ✔️
.ne ✔️
.default ✔️

1:当使用具有多个组条目的组进行数组验证时,出现指示器是“贪婪”的,因为在验证过程中只使用遇到的第一个出现指示器。由于处理这些歧义所涉及的复杂性,后续带有出现指示器的条目将被忽略。为了进行适当的JSON验证,请避免编写类似以下内容的CDDL:[ * a: int, b: tstr, ? c: int ]

2:虽然JSON本身不区分整数和浮点数,但这个crate提供了对更具体的数值CBOR类型的验证能力,前提是它的等效表示形式被JSON允许。有关使用CDDL与JSON数字使用影响的更多详细信息,请参阅标准的附录E:附录E。

3:由于Perl兼容正则表达式(PCREs)比XSD正则表达式更广泛使用,这个crate还提供了对提议的.pcre控制扩展的支持,以替代.regexp运算符(参见讨论CDDL-Freezer提议)。在使用此控制时,请确保您的正则表达式字符串已正确进行JSON转义。

如果您已启用additional-controls功能,下表中的控制表也可供使用

控件操作符 支持
.加上 ✔️
. ✔️
.检测 ✔️
.ABNF ✔️
.ABNF-B 在验证 JSON 时忽略
.特性 ✔️

您可以在验证过程中按以下方式激活功能

use cddl::validate_json_from_str;

let cddl = r#"
  v = JC<"v", 2>
  JC<J, C> =  C .feature "cbor" / J .feature "json"
"#;

let json = r#""v""#;

assert!(validate_json_from_str(cddl, json, Some(&["json"])).is_ok())

与JSON模式和JSON模式语言比较

CDDLJSON模式JSON模式语言都可以用来定义JSON数据结构。然而,开发每种格式的方法差异很大。要找到有关这些格式之间差异的过去讨论的好地方是IETF邮件存档IETF邮件存档,特别是JSON和CBOR列表。这个crate的目的不是为使用CDDL而不是这些格式中的任何一个而辩护,而只是在Rust中提供一个示例实现。

验证CBOR

use cddl::validate_cbor_from_slice;

let cddl = r#"rule = false"#;

let cbor = b"\xF4";

assert!(validate_cbor_from_slice(cddl, cbor).is_ok())

该包还使用 Serdeciborium 来验证 CBOR 数据结构。CBOR 验证是通过松散类型化的 ciborium::value::Value 枚举完成的。除了实现了 JSON 验证器的所有相同功能外,该包还支持验证 CBOR 标签(例如 #6.32(tstr)),CBOR 主要类型(例如 #1.2),表格类型(例如 { [ + tstr ] => int })和字节字符串。同样支持 .bits.cbor.cborseq 控制运算符。

在验证 CBOR 时支持以下标签

标签 支持
tdate= #6.0(tstr) ✔️
时间= #6.1(数字) ✔️
biguint= #6.2(bstr) ✔️
bignint= #6.3(bstr) ✔️
decfrac= #6.4([e10:int,m:integer]) 尚未实现
bigfloat= #6.5([e2:int,m:integer]) 尚未实现
eb64url= #6.21(任何) ✔️
eb64legacy= #6.22(任何) ✔️
eb16= #6.23(任何) ✔️
encoded-cbor= #6.24(bstr) ✔️
uri= #6.32(tstr) ✔️
b64url= #6.33(tstr) ✔️
b64legacy= #6.34(tstr) ✔️
regexp= #6.35(tstr) ✔️
mime-message= #6.36(tstr) ✔️
cbor-任何= #6.55799(任何) ✔️

如果您已启用additional-controls功能,下表中的控制表也可供使用

控件操作符 支持
.加上 ✔️
. ✔️
.检测 ✔️
.ABNF ✔️
.ABNF-B ✔️
.特性 ✔️

您可以通过传递以下方式的功能字符串切片来在验证期间激活功能

use cddl::validate_cbor_from_slice;

let cddl = r#"
  v = JC<"v", 2>
  JC<J, C> =  C .feature "cbor" / J .feature "json"
"#;

let cbor = b"\x02";

assert!(validate_cbor_from_slice(cddl, cbor, Some(&["cbor"])).is_ok())

no_std 支持

在提供堆分配器的情况下,no_std 上下文中只能使用词法分析和解析器。可以通过如下方式在您的 Cargo.toml 文件中禁用默认功能来启用此功能

[dependencies]
cddl = { version = "0.9.3", default-features = false }

实现了尽可能多的零拷贝解析。错误处理和诊断需要分配。

JSON 和 CBOR 验证都依赖于它们各自堆分配的 Value 类型,但由于这些类型在 no_std 上下文中不受支持,因此该包在 no_std 中也不支持。

使用此包的项目

以下是利用此包的一些已知项目

依赖项

~9–20MB
~258K SLoC