markdown_linkify/transform/
docsrs_replacer.rs1use crate::link::Link;
2use crate::LinkTransformer;
3use anyhow::Context;
4use pulldown_cmark::Event;
5use select::document::Document;
6use select::predicate::Name;
7
8#[derive(Debug, Clone, Default)]
9pub struct Docsrs {
10 client: reqwest::blocking::Client,
11}
12
13impl Docsrs {
14 pub fn new() -> Self {
15 Self::default()
16 }
17}
18
19impl LinkTransformer for Docsrs {
20 fn tag(&self) -> String {
21 String::from("docsrs:")
22 }
23
24 fn apply(&self, link: &mut Link) -> anyhow::Result<()> {
26 let url = self
27 .pattern()
28 .replacen(&link.destination, 1, "$i")
29 .to_string();
30 let page = self
31 .client
32 .get(&url)
33 .send()
34 .with_context(|| format!("Failed to access {url}"))?;
35 let doc = Document::from(
36 page.text()
37 .with_context(|| format!("Failed to parse document at {url}"))?
38 .as_str(),
39 );
40 let title = doc
41 .find(Name("title"))
42 .next()
43 .with_context(|| format!("Failed to get title of {url}"))?;
44 let name = title
45 .first_child()
46 .context("First child of title node not found")?
47 .as_text()
48 .context("No title set")?
49 .split_whitespace()
50 .next()
51 .context("Can't split first word of title")?
52 .to_string();
53
54 link.destination = url.to_string().into();
55 if link.title.is_empty() {
56 link.title = url.into();
57 }
58 if link.text.is_empty() {
59 link.text.push(Event::Code(name.into()));
60 }
61 Ok(())
62 }
63}