use super::shared::{opt_span, opt_span_range, GrammarSpan};
use crate::grammar::inlines as grammar;
use crate::parser::ast::{Node, NodeKind};
use nom::IResult;
pub fn parse_autolink(input: GrammarSpan) -> IResult<GrammarSpan, Node> {
let start = input;
let (rest, (uri, is_email)) = grammar::autolink(input)?;
let span = opt_span_range(start, rest);
let uri_span = opt_span(uri);
let node = if is_email {
Node {
kind: NodeKind::Link {
url: format!("mailto:{}", uri.fragment()),
title: None,
},
span,
children: vec![Node {
kind: NodeKind::Text(uri.fragment().to_string()),
span: uri_span,
children: Vec::new(),
}],
}
} else {
Node {
kind: NodeKind::Link {
url: uri.fragment().to_string(),
title: None,
},
span,
children: vec![Node {
kind: NodeKind::Text(uri.fragment().to_string()),
span: uri_span,
children: Vec::new(),
}],
}
};
Ok((rest, node))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke_test_parse_autolink_url() {
let input = GrammarSpan::new("<https://example.com>");
let result = parse_autolink(input);
assert!(result.is_ok(), "Failed to parse URL autolink");
let (rest, node) = result.unwrap();
assert_eq!(rest.fragment(), &"");
if let NodeKind::Link { url, title } = &node.kind {
assert_eq!(url, "https://example.com");
assert!(title.is_none());
} else {
panic!("Expected Link node");
}
assert_eq!(
node.children.len(),
1,
"Autolink should have one text child"
);
}
#[test]
fn smoke_test_parse_autolink_email() {
let input = GrammarSpan::new("<user@example.com>");
let result = parse_autolink(input);
assert!(result.is_ok(), "Failed to parse email autolink");
let (_, node) = result.unwrap();
if let NodeKind::Link { url, title } = &node.kind {
assert_eq!(url, "mailto:user@example.com");
assert!(title.is_none());
} else {
panic!("Expected Link node with mailto: prefix");
}
}
#[test]
fn smoke_test_parse_autolink_not_autolink() {
let input = GrammarSpan::new("just text");
let result = parse_autolink(input);
assert!(result.is_err(), "Should not parse non-autolink as autolink");
}
#[test]
fn smoke_test_parse_autolink_unclosed() {
let input = GrammarSpan::new("<https://example.com");
let result = parse_autolink(input);
assert!(result.is_err(), "Should not parse unclosed autolink");
}
#[test]
fn smoke_test_parse_autolink_position() {
let input = GrammarSpan::new("<https://example.com> and text");
let result = parse_autolink(input);
assert!(result.is_ok());
let (rest, node) = result.unwrap();
assert_eq!(rest.fragment(), &" and text");
assert!(node.span.is_some(), "Autolink should have position info");
let span = node.span.unwrap();
assert_eq!(span.start.offset, 0);
assert!(span.end.offset > span.start.offset);
}
}