2 个版本
0.1.1 | 2022 年 6 月 7 日 |
---|---|
0.1.0 | 2022 年 3 月 16 日 |
#1343 in 解析器实现
160KB
2K SLoC
cmdparse
cmdparse
,正如其名,将用户命令解析成任意的 Rust 类型。
通常,这个crate可以被看作是一个数据反序列化框架。它定义了一种易于交互式输入的语法,并包含将这种格式的输入转换为任意 Rust 类型的工具,以及自动为不完整用户输入建议补全。
它不适用于解析命令行参数,尽管它支持的语法与那些看起来相当相似。相反,它被设计用于解析在应用程序内部交互式输入的命令。当然,你并不局限于这个用例,可以自由地将 cmdparse
作为通用的数据反序列化框架以任何你喜欢的方式使用。
示例
让我们考虑以下示例。它定义了一个名为 MailSendCommand
的 struct 并为它派生了 Parsable
trait。这足以解析它。
use cmdparse::{Parsable, parse};
#[derive(Debug, PartialEq, Eq, Parsable)]
struct MailSendCommand {
text: String,
#[cmd(attr(subject), default = "\"no subject\".to_string()")]
subject: String,
#[cmd(attr(to))]
to: Vec<String>,
}
let input = "\"Hello, world\" --to [email protected] [email protected] --subject Greeting";
let result = parse::<_, MailSendCommand>(input, ())?;
assert_eq!(result, MailSendCommand {
text: "Hello, world".to_string(),
subject: "Greeting".to_string(),
to: vec!["[email protected]".to_string(), "[email protected]".to_string()],
});
此示例演示了 cmdparse
的几个功能
- 只要内部类型是
Parsable
或存在适当的Parser
,就可以自动为任意 struct 或 enum 派生解析功能。(要了解可解析和解析器之间的区别,请参阅这些 trait 的文档)。 - 派生的解析器是可配置的:您可以指定字段是必需的还是可选的。可以通过名称属性(
--
符号)指定可选字段。它们可以明确指定默认值(如subject
字段的默认属性所示)或不指定(to
字段默认为空向量,如其Default
实现)。 - 可解析的值可以包含嵌套的可解析值:
MailSendCommand 是可解析的,它包含一个可解析的
Vec
,它重复解析可解析的String
。注意cmdparse
如何识别当它遇到既不是String
也不是Vec
所识别的属性时列表中的电子邮件地址结束。
cmdparse
可以生成完成建议
use cmdparse::complete;
use std::collections::BTreeSet;
let suggestions = complete::<_, MailSendCommand>("\"Hello, world\" --", ());
assert_eq!(suggestions, BTreeSet::from(["to".into(), "subject".into()]));
它还支持解析枚举。对于枚举,它期望一个区分符(由 Parsable
派生宏自动转换为短横线命名法)
use cmdparse::{parse, Parsable};
#[derive(Debug, PartialEq, Eq, Parsable)]
enum Priority {
High,
Medium,
Low,
}
impl Default for Priority {
fn default() -> Self {
Priority::Medium
}
}
#[derive(Debug, PartialEq, Eq, Parsable)]
enum Command {
AddTask(String, #[cmd(attr(priority))] Priority),
Remove(usize),
}
assert_eq!(
parse::<_, Command>("add-task parse-all-commands", ())?,
Command::AddTask("parse-all-commands".to_string(), Priority::Medium),
);
assert_eq!(
parse::<_, Command>("add-task enjoy-your-day --priority high", ())?,
Command::AddTask("enjoy-your-day".to_string(), Priority::High),
);
assert_eq!(parse::<_, Command>("remove 1", ())?, Command::Remove(1));
语法
cmdparse
支持的语法相对简单。解析器将输入视为由空格分隔的字符序列。标记是任何由空格分隔的字符序列。如果您想在标记中包含空格,可以将输入的任何子串放在一对引号(双引号或单引号)中;cmdparse
支持使用反斜杠(\
)在引号标记中转义这些符号。
输入可以包含以井号(#
)开始的注释。引号标记内的井号不被认为是注释的开始。
标记和属性的含义高度特定于每个解析器。通常,每个解析器按顺序消费标记,直到每个必需字段的值都填充完毕。它也可以按任意顺序和任意位置处理属性。
由于命令语法的特性,解析可能会显得模糊。例如,cmdparse
可以解析嵌套结构,如Vec<Vec<u32>>
。对于最终用户来说,可能很难解释数字序列(它们都将放入外部向量的唯一项中)。最好是设计简单的命令,避免高度嵌套的结构,以获得更好的用户体验。在某些情况下,复杂性是不可避免的。在这种情况下,用户可能会发现使用括号((
和 )
)将属于同一数据结构的标记分组是有用的。这样,用户可以将一个值vec![vec![1, 2], vec![3, 4, 5]]
作为(1 2) (3 4 5)
。
有关标记化和解析算法的更多详细信息,请参阅tokens
模块和Parser
trait的文档。
许可证
根据以下任何一个许可进行许可:
- Apache License, Version 2.0 (LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
任选其一。
贡献
除非您明确说明,否则根据Apache-2.0许可中定义的,您有意提交以包含在工作中的任何贡献,都将根据上述条款进行双重许可,不附加任何额外条款或条件。
依赖关系
~1.5MB
~37K SLoC