3 个版本
使用旧 Rust 2015
0.1.3 | 2017 年 12 月 18 日 |
---|---|
0.1.2 | 2017 年 12 月 12 日 |
0.1.1 | 2017 年 9 月 14 日 |
#611 在 Cargo 插件
24KB
346 行
cargo docgen - 一个 Rust 文档测试助手
原因
文档测试 是在 crates.io 上发布优秀的 Rust 包不可或缺的一部分。引用 Rust 书籍第一版的内容
没有比带有示例的文档更好的了。没有比实际上不工作的示例更糟糕的了
每个项(模块、函数、方法等)都应该有一个既 编译 又 运行测试 的示例。
然而,如果你浏览 docs.rs 上的资源,并查看一些 11,000 多个包,你会看到许多甚至没有尝试提供任何文档,这令人失望,并使你不得不 实际阅读源代码。这部分原因是人类的天性,或者至少是程序员难以从代码切换到英文的天性,但很大一部分原因是好的文档是 辛苦的工作。不仅格式化文档测试令人烦恼,而且运行 cargo test
来运行所有测试可能会花费相当长的时间,即使是小型项目也是如此。
文档 指南 非常全面且相当严格。 cargo docgen
的目标是使准备可工作的测试并将其嵌入源代码变得更简单。
一个简单的例子
假设你希望发布你伟大的作品,包 life
。你希望记录函数 life::answer
。在 life
项目的某个子目录中编写一段小代码片段,如下所示(我个人创建一个 scratch
目录并将其放入 .gitignore
)
// answer.rs
let a = life::answer();
assert_eq!(a, 42);
然后运行 cargo docgen
$ cargo docgen answer.rs
****** Copy and paste this output into your code ******
/// ```
/// let a = life::answer();
/// assert_eq!(a, 42);
/// ```
首先,将显式创建一个小型的Rust程序,使用doctest规则,使用cargo run --example
运行此代码,并适当地注释结果。
你可以在真实编辑器中键入doc测试,立即运行它,并将结果直接粘贴到你的代码中。(我不知道其他人,但我喜欢在代码感知编辑器中键入Rust,而且我不喜欢等待发现我是否不可避免地犯了错误。)
此注释适用于任何非模块级别的代码项。如果我输入cargo docgen -m answer.rs
,结果将格式化为模块级别的示例。
//! ```
//! let a = life::answer();
//! assert_eq!(a, 42);
//! ```
你可以使用--indent
缩进结果。我倾向于使用-i4
,因为我喜欢空格,但-i1t
将缩进一个制表符,依此类推。(混合空格和制表符是一种邪恶。)
支持问号运算符
考虑这个测试lua-patterns的代码片段。
let mut pat = lua_patterns::LuaPattern::new_try("^%s*$").unwrap();
assert!(pat.matches(" "));
在小示例中看到unwrap
很常见,但它既令人讨厌又具有误导性,因为编写良好的代码中几乎不会出现。在现实生活中,我们使用问号运算符来处理错误。正如指南所说:“不管你是否喜欢,示例代码经常被用户原封不动地复制。取消错误包装应该是一个用户需要做出的有意识的决策。”
这就是--question
标志(简称-q
)的目的。
因此,你应该这样写
let mut pat = lua_patterns::LuaPattern::new_try("^%s*$")?;
assert!(pat.matches(" "));
并且cargo docgen -q -i4 new_try.rs
将生成以下代码
/// ```
/// # use std::error::Error;
/// #
/// # fn run() -> Result<(),Box<Error>> {
/// let mut pat = lua_patterns::LuaPattern::new_try("^%s*$")?;
/// assert!(pat.matches(" "));
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # run().unwrap();
/// # }
/// ```
这是推荐展示可能发生错误的代码的方式,它有很多样板代码。doc测试语法允许使用#
隐藏行,因此渲染的文档中只会出现实际的代码片段。(这里我们使用了一个方便的事实,即任何 Error
类型都会转换为Box<Error>
)
编译并运行此代码片段耗时1.2秒——整个项目在时钟时间中运行cargo test
耗时14.7秒!直接将完整的注释代码输入到库源代码中会更长时间,并且会更痛苦。
不是测试的示例
doc测试(就像其他Rust测试一样)由一系列断言组成。你可以使用println!
,但测试运行器会吞没这个输出。cargo docgen
确实会打印输出,但会发出警告。
一些示例应该被编译,但不应该运行。这里我们处理《第一版书》中的明显糟糕的测试代码示例。
$ cat loop.rs
loop {
println!("Hello, world");
}
$ cargo docgen -n loop.rs
****** Copy and paste this into your code ******
/// ```rust,no_run
/// loop {
/// println!("hello, world");
/// }
/// ```
测试和格式化Markdown
--module-doc
(简称-M
)标志让你可以处理一个包含小doc测试片段的整个Markdown文件。这里有一个愚蠢的例子
这里应该是任何可以安全编辑的文本。只有当片段改变时才会运行片段。
use lua_patterns::*; let mut pat = LuaPattern::new_try("^%s*$")?; assert!(pat.matches(" ")); assert!(! pat.matches(" x "));
然后文本继续。
这显示了默认情况下匹配是“未锚定的”。
let mut pat = lua_patterns::LuaPattern::new("boo"); assert!( pat.matches("boo") ); assert!( pat.matches(" boo ") );
还有另一个
for i in 0..4 { println!("gotcha! {}",i); }
这几乎是我们熟悉和喜爱的 GitHub 风格的 Markdown,只有一个小小的变化。如果一个文档测试使用了问号操作符,例如 cargo codegen
需要知道,以便生成必要的模板代码。由于在源代码中可靠地检测 ?
是困难的(它可能位于注释或字符串中),我选择了明确的方法,其中常用的保护 "rust" 变为 "
rust?"。您也可以像以前一样说 "```rust?n" 来表示 '不运行'。
运行 cargo docgen -M doc.md
,在 运行每个代码片段 后给出结果。
//! This should be any text
//! whatsoever which can be edited safely. Snippets are only
//! run if they change:
//!
//! ```
//! # use std::error::Error;
//! #
//! # fn run() -> Result<(),Box<Error>> {
//! use lua_patterns::*;
//! let mut pat = LuaPattern::new_try("^%s*$")?;
//! assert!(pat.matches(" "));
//! assert!(! pat.matches(" x "));
//! # Ok(())
//! # }
//! #
//! # fn main() {
//! # run().unwrap();
//! # }
//! ```
//! and the text continues.
//!
//! This shows how by default matches are 'unanchored':
//!
//! ```
//! let mut pat = lua_patterns::LuaPattern::new("boo");
//! assert!( pat.matches("boo") );
//! assert!( pat.matches(" boo ") );
//! ```
//!
//! And another:
//! ```
//! for i in 0..4 {
//! println!("gotcha! {}",i);
//! }
//! ```
//!
此外,这些代码片段将被缓存(之后查看 'doc.cache'),后续运行将 仅 重新运行实际发生变化的文档测试。
好的 Rust 文档测试很难 打字,我希望这个实用程序能更容易让其他人编写更好的、功能性的文档。要安装,只需使用 cargo install cargo-docgen
。
依赖关系
~86KB