8 个版本
0.1.1 | 2022年12月26日 |
---|---|
0.1.0 | 2022年12月26日 |
0.0.8 | 2019年6月24日 |
0.0.5 | 2018年6月11日 |
0.0.2 | 2017年2月6日 |
#756 in 解析器实现
每月 22 次下载
用于 4 个 crate (3 直接)
765KB
20K SLoC
Yamlette
Rust 实现的全面 YAML 1.2 处理器
特性
- 完全支持 YAML 1.2 规范
- 覆盖 YAML 1.2 规范中每个示例的测试
- 方便的宏,用于 YAML 读取和写入
- 控制写入模式中的输出格式
- 通过 trait 实现,轻松反序列化自己的类型(
FromPointer<'a>
) - 尽管不稳定,但可以通过 trait 实现序列化自己的类型(orchestra::chord::Chord)
- 实验性的多线程模型(尽管目前比单线程实现慢得多,不推荐使用)
示例
yamlette!
宏执行所有工作,因此您可以使用库而无需了解其内部工作方式。
主要思想是描述您正在处理的数据结构,而不是玩弄对象和方法。
示例之后有格式说明,因为 Reader 和 Writer 都有类似的格式。
基本读取示例
#[macro_use]
extern crate yamlette;
const SRC_YAML: &'static str = r#"
sequence:
- one
- two
mapping:
? sky
: blue
sea : green
"#;
fn main () {
yamlette! ( read ; SRC_YAML ; [[
{
"sequence" => (list seq:Vec<String>),
"mapping" => {
"sky" => (sky_color:&str),
"sea" => (sea_color:String)
}
}
]] );
assert! (seq.is_some ());
let seq = seq.unwrap ();
assert_eq! (seq.len (), 2);
assert_eq! (seq[0], "one");
assert_eq! (seq[1], "two");
assert_eq! (sky_color, Some ("blue"));
assert_eq! (sea_color, Some (String::from ("green")));
}
其第一个参数不是一个变量,而是一个文字,表示我们需要在这里调用读取功能。
第二个参数是读取的数据源。它可以有以下类型
&'static str
String
Vec<u8>
&[u8]
std::fs::File
std::os::unix::net::UnixStream
& std::os::unix::net::UnixStream
std::net::TcpStream
& std::net::TcpStream
Box<std::io::Read>
&mut std::io::Read
std::io::BufReader
- 任何其他实现了
skimmer::reader::IntoReader
trait 的对象
第三个参数是数据结构描述。其格式在以下示例之后描述。
yamlette!
宏在作为读取器调用时默认不返回任何内容。
基本写入示例
#[macro_use]
extern crate yamlette;
use std::collections::BTreeMap;
const TGT_YAML: &'static str =
r#"name: Martin D'vloper
job: Developer
employed: true
foods:
- Apple
- Orange
- Strawberry
- Mango
languages:
pascal: Lame
perl: Elite
python: Elite
education: "4 GCSEs\n3 A-Levels\nBSc in the Internet of Things"
"#;
fn main () {
let name = "Martin D'vloper";
let employed = true;
let foods = vec! ["Apple", "Orange", "Strawberry", "Mango"];
let mut languages = BTreeMap::new ();
languages.insert ("pascal", "Lame");
languages.insert ("perl", "Elite");
languages.insert ("python", "Elite");
let education = "4 GCSEs\n3 A-Levels\nBSc in the Internet of Things";
let string = yamlette! ( write ; [[ {
"name": name,
"job": "Developer",
"employed": employed,
"foods": foods,
"languages": languages,
"education": education
} ]] ).ok ().unwrap ();
assert_eq! (string, TGT_YAML);
}
其第一个参数不是一个变量,而是一个文字,表示我们需要在这里调用写入功能。
第二个参数是数据结构描述。其格式在以下示例之后描述。
yamlette!
宏在作为写入器调用时,默认返回 Result<String, yamlette::orchestra::OrchError>
实例。
自定义类型读取示例
您只需为您的类型实现 FromPointer<'a>
特性
extern crate yamlette;
use self::yamlette::book::extractor::pointer::Pointer;
use self::yamlette::book::extractor::traits::FromPointer;
#[derive (PartialEq, Eq, Debug)]
struct State {
pub state: bool,
pub transition: u8,
pub brightness: u8
}
impl State {
pub fn new (state: bool, transition: u8, brightness: u8) -> State {
State {
state: state,
transition: transition,
brightness: brightness
}
}
}
impl<'a> FromPointer<'a> for State {
fn from_pointer (pointer: Pointer<'a>) -> Option<Self> {
yamlette_reckon! ( ptr ; Some (pointer) ; {
(state:bool),
(transition:u8),
(brightness:u8)
} );
Some (State {
state: if let Some (s) = state { s } else { false },
transition: if let Some (t) = transition { t } else { 0u8 },
brightness: if let Some (b) = brightness { b } else { 0u8 }
})
}
}
格式描述
常见事项
格式背后的主要思想是,您可以以类似JSON的格式表达您数据的结构。然而,YAML流可能包含多个文档,因此我们也将它们列举为顶级列表,并用方括号括起来。还有一个方括号的顶层,以便Rust宏引擎可以读取所有数据描述作为一个单独的标记树。
这就是为什么我们总是有两个方括号在顶部的原因
- 第一个是为Rust宏引擎
- 第二个是包含一个YAML文档的(我们可以有很多这样的,但我们始终至少有一个)
这意味着我们可以用以下结构来描述一个空YAML文档: [[]]
。两个空YAML文档将是: [ [], [] ]
等。
在文档中,我们开始描述实际的数据节点。有三种可能的节点类型
- 字典(Map),可以用花括号描述:
{}
- 序列(List),可以用方括号描述:
[]
- 标量(Node),可以用括号描述(有时甚至不需要):
()
数据类型之间的特定差异在读取和写入时不同。
格式的读取器特定
当我们读取一个字典时,我们会列举其键和值(这些值本身也是节点)。
有两种方式可以列举一个字典
{ key => value }
- 通过键值查找值(应用<key as PartialEq>::eq
){ key > value }
- 简单地遍历字典的键和值,并按原始顺序分配它们的值
有意未实现JSONish形式的 { key : value }
,因为在项目当前阶段尚不清楚哪种行为应该是默认的。
当涉及到标量值时,有几个选项:我们将它用括号括起来
(var:type)
- 变量名及其类型,读取器应尝试将其转换为读取的值(list var:type)
- 将节点作为集合处理;类型应该实现yamlette::book::extractor::traits::List
特性(dict var:type)
- 将节点作为集合处理;类型应该实现yamlette::book::extractor::traits::Dict
特性(call FnOnce [, FnOnce[, ...]])]
- 适用于高级用户;在节点上调用多个自定义回调函数(foreach FnOnce [, FnOnce[, ...]]]
- 适用于高级用户;在节点的所有兄弟节点上调用多个自定义回调函数
幸运的是,序列没有特别之处。
编写格式的具体细节
当我们编写字典时,我们只需用冒号和逗号枚举其键和值,就像在JSON中一样
{键:值}
当涉及到标量值时,我们只需提供一个值,无需任何语法糖。避免将表达式或代码块传递给宏是一个好习惯,因为它可能会干扰宏的格式本身。然而,如果您需要提供一个表达式,您可能希望用括号将其括起来。
当我们编写内容时,我们可能希望选择一些样式,以便处理器生成输出。
还有两件事需要知道
- 指令
- 样式
样式适用于所有内容,并且它们可以放在
- 宏标记打开的方括号之后(这将适用于所有文档)
- 文档打开的方括号之后(这将适用于文档中的所有节点)
- 列表打开的方括号之后(这将适用于列表中的所有节点,但不包括列表本身)
- 字典打开的大括号之后(这将适用于字典中的所有节点,但不包括字典本身)
- 括号之后(对于节点)
指令适用于YAML文档,并且可以放在
- 宏标记打开的方括号之后(这将适用于所有文档)
- 文档打开的方括号之后(这将适用于特定文档)
如果您需要在同一级别设置样式和指令,则样式先于指令
[ % YAML, BORDER_TOP, NO_BORDER_BOT => # FLOW => [ ... ]]
指令列表应以百分号(%)开头,以等于加大于号(=>)结尾,这是由于某些Rust宏引擎的限制,并且不会干扰现有的Rust语言语法。
样式列表应以井号(#)开头,以等于加大于号(=>)结尾。
可能的指令
- YAML - 打印出 %YAML 指令
- BORDER_TOP - 打印出文档的上边框(---)
- 文档底部边框 - 打印文档的底部边框 (...), 在存在多个 YAML 文档的情况下自动生成
- NO_YAML - 忽略 YAML 指令
- NO_BORDER_TOP - 忽略 BORDER_TOP 指令
- NO_BORDER_BOT - 忽略 BORDER_BOT 指令
- (TAG ; handle , prefix ) - 发布自定义 %TAG 指令
有两种类型的样式:通用样式和模型样式。模型样式仅适用于单一数据类型(数据模型),如 !!str 或 !!int。通用样式适用于多个数据类型(数据模型)。
已经实现了一些通用样式
yamlette::model::style::Indent
- 允许更改缩进大小(默认为2个空格)yamlette::model::style::Flow
- 流模式(类似 JSON 模式)yamlette::model::style::Compact
- 尽可能进行紧凑输出(眼睛刺痛模式)yamlette::model::style::Multiline
- 尽可能多地使用换行符(与 FLOW 模式一起使用很有用)yamlette::model::style::IssueTag
- 发布节点标签(是的,你说对了,!!str、!!int、!!map、!!timezone 等)yamlette::model::style::RespectThreshold
- 如果行太长,则换行yamlette::model::style::Threshold
- RespectThreshold 模式下每行的最大字符数
模型样式可能会更改某些格式,主要取决于使用案例。尽管如此,已经实现了一些
yamlette::model::yaml::str::ForceQuotes
- 用引号括住字符串(即使没有特殊字符或换行符)yamlette::model::yaml::str::PreferDoubleQuotes
- 如果 ForceQuotes,则使用"
而不是'
- 其他一些可能在库的新版本中实现(例如,为 !!int、!!float 和 !!timestamp 实现数字格式化)
以下是一个描述的示例
#[macro_use]
extern crate yamlette;
use std::collections::BTreeMap;
const TGT_YAML: &'static str =
r#"%YAML 1.2
%TAG !custom-bool-tag! tag:yaml.org,2002:boo
---
name: Martin D'vloper
job: 'Developer'
employed: !custom-bool-tag!l true
foods: [ Apple, Orange, Strawberry, Mango ]
languages: {
pascal: Lame,
perl: Elite,
python: Elite
}
...
"#;
fn main () {
let name = "Martin D'vloper";
let employed = true;
let foods = vec! ["Apple", "Orange", "Strawberry", "Mango"];
let mut languages = BTreeMap::new ();
languages.insert ("pascal", "Lame");
languages.insert ("perl", "Elite");
languages.insert ("python", "Elite");
use yamlette::model::style::{ FLOW, MULTILINE, ISSUE_TAG, Indent };
use yamlette::model::yaml::str::FORCE_QUOTES;
let string = yamlette! ( write ; [
% YAML, ( TAG ; "!custom-bool-tag!" , "tag:yaml.org,2002:boo" ) =>
[
{ # FLOW =>
"name": name,
"job": ( # FORCE_QUOTES => "Developer" ),
"employed": ( # ISSUE_TAG => employed ),
"foods": foods,
"languages": ( # MULTILINE, Indent (4) => languages )
}
]
] ).ok ().unwrap ();
assert_eq! (string, TGT_YAML);
}
许可协议
许可协议:Double: MIT / Apache 许可证, 版本 2.0
依赖项
~1.5MB
~32K SLoC