v6/src/generator/markdown/link_decorations.rs

124 lines
3.5 KiB
Rust

use pulldown_cmark::{html, CowStr, Event, Tag};
use url::Url;
pub struct LinkDecorations<'a, I: Iterator<Item = Event<'a>>> {
iter: I,
}
pub fn new<'a, I: Iterator<Item = Event<'a>>>(iter: I) -> LinkDecorations<'a, I> {
LinkDecorations { iter }
}
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkDecorations<'a, I> {
type Item = Event<'a>;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
Some(Event::Start(Tag::Link(_, href, title))) => {
let link = href_to_pretty_link(&href);
let mut s = format!(r#"<a href="{}" data-link="{}"#, href, link);
if !title.is_empty() {
s.push_str(" title=\"");
s.push_str(title.as_ref());
}
s.push_str("\">");
html::push_html(&mut s, LinkContents(&mut self.iter));
s.push_str("</a>");
Some(Event::Html(CowStr::Boxed(s.into_boxed_str())))
}
e => e,
}
}
}
struct LinkContents<'a, 'b, I: Iterator<Item = Event<'a>>>(&'b mut I);
impl<'a, 'b, I: Iterator<Item = Event<'a>>> Iterator for LinkContents<'a, 'b, I> {
type Item = Event<'a>;
fn next(&mut self) -> Option<Self::Item> {
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#"<p><a href="bar" data-link="bar">foo</a></p>"#
);
}
}