use super::shared::{opt_span, opt_span_range, GrammarSpan};
use crate::grammar::blocks::marco_sliders::MarcoSlideDeck;
use crate::parser::ast::{Document, Node, NodeKind};
pub fn parse_marco_slide_deck<F>(
deck: MarcoSlideDeck<'_>,
full_start: GrammarSpan<'_>,
full_end: GrammarSpan<'_>,
depth: usize,
mut parse_blocks_fn: F,
) -> Result<Node, Box<dyn std::error::Error>>
where
F: FnMut(&str, usize) -> Result<Document, Box<dyn std::error::Error>>,
{
let deck_span = opt_span_range(full_start, full_end);
let mut root = Node {
kind: NodeKind::SliderDeck {
timer_seconds: deck.timer_seconds,
},
span: deck_span,
children: Vec::new(),
};
for slide in deck.slides {
let body = slide.content.fragment();
let slide_doc = match parse_blocks_fn(body, depth + 1) {
Ok(doc) => doc,
Err(e) => {
log::warn!("Failed to parse slide content: {}", e);
Document::new()
}
};
root.children.push(Node {
kind: NodeKind::Slide {
vertical: slide.vertical,
},
span: opt_span(slide.content),
children: slide_doc.children,
});
}
Ok(root)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::grammar::shared::Span;
fn mock_parse_blocks(
input: &str,
_depth: usize,
) -> Result<Document, Box<dyn std::error::Error>> {
let mut doc = Document::new();
if !input.trim().is_empty() {
doc.children.push(Node {
kind: NodeKind::Text(input.to_string()),
span: None,
children: Vec::new(),
});
}
Ok(doc)
}
#[test]
fn smoke_test_parse_marco_slide_deck_builds_ast() {
let raw = "@slidestart\nA\n---\nB\n@slideend\n";
let span = Span::new(raw);
let (rest, deck) = crate::grammar::blocks::marco_sliders::marco_slide_deck(span)
.expect("grammar parse failed");
let node = parse_marco_slide_deck(deck, Span::new(raw), rest, 0, mock_parse_blocks)
.expect("parser failed");
assert!(matches!(node.kind, NodeKind::SliderDeck { .. }));
assert_eq!(node.children.len(), 2);
assert!(matches!(node.children[0].kind, NodeKind::Slide { .. }));
}
}