use super::shared::{opt_span_range, GrammarSpan};
use crate::grammar::inlines as grammar;
use crate::parser::ast::{Node, NodeKind};
use nom::IResult;
use nom::Input;
pub fn parse_inline_html(input: GrammarSpan) -> IResult<GrammarSpan, Node> {
let start = input;
let (rest, _content) = grammar::inline_html(input)?;
let start_offset = start.location_offset();
let end_offset = rest.location_offset();
let html_len = end_offset - start_offset;
let html = start.take(html_len).fragment().to_string();
let span = opt_span_range(start, rest);
let node = Node {
kind: NodeKind::InlineHtml(html),
span,
children: Vec::new(),
};
Ok((rest, node))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke_test_parse_inline_html_tag() {
let input = GrammarSpan::new("<span>");
let result = parse_inline_html(input);
assert!(result.is_ok(), "Failed to parse inline HTML");
let (rest, node) = result.unwrap();
assert_eq!(rest.fragment(), &"");
if let NodeKind::InlineHtml(html) = &node.kind {
assert_eq!(html, "<span>");
} else {
panic!("Expected InlineHtml node");
}
assert!(
node.children.is_empty(),
"Inline HTML should not have children"
);
}
#[test]
fn smoke_test_parse_inline_html_self_closing() {
let input = GrammarSpan::new("<br/>");
let result = parse_inline_html(input);
assert!(result.is_ok());
let (_, node) = result.unwrap();
if let NodeKind::InlineHtml(html) = &node.kind {
assert_eq!(html, "<br/>");
}
}
#[test]
fn smoke_test_parse_inline_html_with_attributes() {
let input = GrammarSpan::new(r#"<a href="url">"#);
let result = parse_inline_html(input);
assert!(result.is_ok());
let (_, node) = result.unwrap();
if let NodeKind::InlineHtml(html) = &node.kind {
assert!(html.contains("href"));
}
}
#[test]
fn smoke_test_parse_inline_html_not_html() {
let input = GrammarSpan::new("just text");
let result = parse_inline_html(input);
assert!(result.is_err(), "Should not parse non-HTML as inline HTML");
}
#[test]
fn smoke_test_parse_inline_html_position() {
let input = GrammarSpan::new("<span> and text");
let result = parse_inline_html(input);
assert!(result.is_ok());
let (rest, node) = result.unwrap();
assert_eq!(rest.fragment(), &" and text");
assert!(node.span.is_some(), "Inline HTML should have position info");
let span = node.span.unwrap();
assert_eq!(span.start.offset, 0);
assert!(span.end.offset > span.start.offset);
}
#[test]
fn smoke_test_parse_inline_html_img_tag() {
let input = GrammarSpan::new(r#"<img src="test.png" alt="test" />"#);
let result = parse_inline_html(input);
assert!(result.is_ok(), "Failed to parse img tag");
let (rest, node) = result.unwrap();
assert_eq!(rest.fragment(), &"");
if let NodeKind::InlineHtml(html) = &node.kind {
assert_eq!(html, r#"<img src="test.png" alt="test" />"#);
println!("Parsed img HTML: {}", html);
} else {
panic!("Expected InlineHtml node, got {:?}", node.kind);
}
assert!(node.span.is_some(), "Img tag should have position info");
let span = node.span.unwrap();
println!(
"Span: L{}:C{}-L{}:C{} (offset {}-{})",
span.start.line,
span.start.column,
span.end.line,
span.end.column,
span.start.offset,
span.end.offset
);
}
}