use pulldown_cmark::{html, CowStr, Event, Tag}; use url::Url; pub struct LinkDecorations<'a, I: Iterator>> { iter: I, } pub fn new<'a, I: Iterator>>(iter: I) -> LinkDecorations<'a, I> { LinkDecorations { iter } } impl<'a, I: Iterator>> Iterator for LinkDecorations<'a, I> { type Item = Event<'a>; fn next(&mut self) -> Option { match self.iter.next() { Some(Event::Start(Tag::Link(_, href, title))) => { let link = href_to_pretty_link(&href); let mut s = format!(r#""); html::push_html(&mut s, LinkContents(&mut self.iter)); s.push_str(""); Some(Event::Html(CowStr::Boxed(s.into_boxed_str()))) } e => e, } } } struct LinkContents<'a, 'b, I: Iterator>>(&'b mut I); impl<'a, 'b, I: Iterator>> Iterator for LinkContents<'a, 'b, I> { type Item = Event<'a>; fn next(&mut self) -> Option { match self.0.next() { Some(Event::End(Tag::Link(_, _, _))) => None, e => e, } } } fn href_to_pretty_link<'a>(href: &CowStr<'a>) -> CowStr<'a> { match Url::parse(href) { Ok(url) => { let mut s = String::new(); let host = url.host_str(); if let Some(h) = host { if h.starts_with("www.") { s.push_str(&h[4..]); } else { s.push_str(h); } } let path = url.path(); if path != "/" { s.push_str(path); } if host.map(|s| s.ends_with("youtube.com")).unwrap_or(false) && path == "/watch" { s.push('?'); s.push_str(url.query().unwrap()); } if s.len() > 40 { s = s.chars().take(40).collect(); s.push('…'); } CowStr::Boxed(s.into_boxed_str()) } Err(_) => { // if there was an error parsing the url, we just pass it through unchanged href.clone() } } } #[cfg(test)] mod tests { use pulldown_cmark::{html, CowStr, Parser}; fn pretty_link<'a>(s: &'a str) -> CowStr<'a> { super::href_to_pretty_link(&CowStr::Borrowed(s)) } fn render(s: &str) -> String { let mut out = String::new(); let parser = Parser::new(s); let heading_anchors = super::new(parser); html::push_html(&mut out, heading_anchors); out } #[test] fn test_href_to_pretty_link() { assert_eq!( pretty_link("https://asahilinux.org/2022/03/asahi-linux-alpha-release/").as_ref(), "asahilinux.org/2022/03/asahi-linux-alpha…" ); assert_eq!( pretty_link("https://www.youtube.com/watch?v=MFzDaBzBlL0").as_ref(), "youtube.com/watch?v=MFzDaBzBlL0" ); assert_eq!( pretty_link("https://www.youtube.com/watch?v=MFzDaBzBlL0&t=10").as_ref(), "youtube.com/watch?v=MFzDaBzBlL0&t=10" ); } #[test] fn test_link_decorations() { assert_eq!( render("[foo](bar)").trim(), r#"

foo

"# ); } }