创建健壮、可重用的链接检查器

2020-05-04 15:18:04

我最成功的Rust项目之一是一个名为mdbook-linkcheck的无与伦比的小程序,下载量超过68842次。这是一个用于mdbook的链接检查器,该工具支持Rust社区中的大量文档,包括Rust Programming Language和Rustc Dev Book。

作为一个例子,我最近在Chalk的文档中发现了几个断开的链接。当该工具在您的标记中检测到断开的链接时,它将发出错误消息,指示您链接被定义的位置,并解释问题所在。

这个工具已经存在了一段时间了,并且运行得很好,所以当我前几天修复一个bug时,我决定是时候将核心逻辑提取到一个独立的库中,供其他人使用了。

本文中编写的代码可在GitHub上获得,并在crates.io上发布。您可以随意浏览和窃取代码或灵感。

如果您觉得这很有用或发现了错误,请在博客的问题跟踪器上让我知道!

当你开始建图书馆的时候,想想图书馆想要解决什么问题是很好的。这样,您就知道哪些功能属于该库,更重要的是,知道哪些不属于该库。

链接检查器机箱的主要目标是查找文档中的链接,并检查它们是否指向有效的内容。这里似乎有两个概念:

scanner-某种可以使用文本并返回找到的链接流的函数。

验证器-可以接受一批链接并告诉你它们是否有效的东西。

出于诊断目的,我们还想知道每个链接在源文档中出现的位置。因此,链接不只是一个字符串,还需要拖动它的跨度。

Codesan机箱包含许多管理源代码和发出诊断信息的强大工具,所以我想我会非常依赖它。

从长远来看,包括大多数流行格式的扫描仪会很好,但是为了便于管理,我现在将其限制为纯文本和降价。我想HTML也会是一个很好的补充,因为它可以让人们检查他们的网站,但我会把这留作稍后的练习。

验证Web链接应该相当容易,我们可以向适当的网站发送GET请求,我们的Web客户端(可能是reqwest)会让我们知道是否有死链接。

我想我应该从纯文本开始,因为这是最简单的。我们想要创建一种迭代器,它可以生成所有类似于URL的文本片段。

最初我以为这只是一个编写正则表达式并从regexrate映射匹配迭代器的情况,但结果发现URL并非易事。

在谷歌上搜索了大约10分钟,浏览了几十个StackOverflow问题后,我没有找到一个表达式,它可以匹配我预期的所有URL类型,同时也避免了标点符号,如句号或当链接在括号中时,以及检测到的链接没有方案(例如,/readme.md而不是file:/README.md).。

有些人在遇到问题时会想:“我知道,我会用通俗的表达方式。现在他们有两个问题。”

对我来说幸运的是,这个问题已经解决了,可以在crates.io上找到Linkify板条箱!

纵观源代码,他们似乎已经编写了很多手动代码来考虑URL如何嵌入文本正文。这主要包括扫描特定的“触发器字符”(对于URL,@对于电子邮件地址),然后回溯以找到项目的开头。还有很多测试来确保只检测到所需的文本为匹配。

//src/scanner/Platext.rs使用Crate::codesan::span;使用Linkify::{LinkFinder,LinkKind};pub FN纯文本(src:&;str)->;Impl Iterator<;Item=(&;str,Span)>;+';_{LinkFinder::New().种(&;[LinkKind::url]).link(Src).map(|link|{(link.as_str(),span::new(link.start()as u32,link.end()as u32),)}。

//src/scanner/Platext.rs#[cfg(Test)]mod test{使用SUPER::*;#[TEST]fn detect_urls_in_ome_text(){let src=";hello http://localhost/world。这是file://some/text.";;let应该_be=vec![(";http://localhost/";,Span::New(6,23)),(";file://some/text";,span::New(39,55)),];let Get:vec<;_>;=明文(Src).Collect();assert_eq!(得到,应该是);}}。

为了解析markdown,我的首选库是Pull-Down-cmark.这将公开一个基于迭代器的API,从而产生“段落标记开始”、“内联代码结束”、“水平标尺”等事件。

此API级别相当低,如果您想要创建文档的某种语义模型(例如,DOM),则需要自己做大量工作,但如果您只想像我们一样浏览文档并提取特定片段,