core_markdown/renderer.rs
1// Released under the MIT License.
2// Copyright, 2026, by Samuel Williams.
3
4//! Traits for rendering Markdown AST nodes.
5mod html_renderer;
6mod markdown_renderer;
7
8pub use html_renderer::HtmlRenderer;
9pub use markdown_renderer::MarkdownRenderer;
10
11use crate::mdast::Node;
12use alloc::string::String;
13
14/// A renderer for an AST node tree.
15///
16/// Implement [`render_node`][Self::render_node] to decide how each node is
17/// emitted. Call [`render_children`][Self::render_children] for transparent
18/// containers or when a node's children should be rendered recursively.
19/// Renderers choose their output format and escaping rules.
20///
21/// This is separate from [`crate::to_html`], which parses Markdown source and
22/// renders the parser's events with the built-in HTML compiler.
23///
24/// # Example
25///
26/// ```
27/// use core_markdown::{mdast::Node, Renderer};
28///
29/// struct PlainText;
30///
31/// impl Renderer for PlainText {
32/// fn render_node(&mut self, node: &Node) -> String {
33/// match node {
34/// Node::Text(text) => text.value.clone(),
35/// Node::InlineCode(code) => code.value.clone(),
36/// Node::Code(code) => code.value.clone(),
37/// Node::Break(_) => "\n".into(),
38/// Node::Image(image) => image.alt.clone(),
39/// Node::ImageReference(image) => image.alt.clone(),
40/// Node::Definition(_) | Node::FootnoteReference(_) | Node::ThematicBreak(_) => {
41/// String::new()
42/// }
43/// _ => self.render_children(node),
44/// }
45/// }
46/// }
47///
48/// let node = Node::Fragment(core_markdown::mdast::Fragment {
49/// children: vec![Node::Text(core_markdown::mdast::Text {
50/// value: "hello".into(),
51/// position: None,
52/// })],
53/// });
54/// let mut renderer = PlainText;
55/// let plain_text = node.render_with(&mut renderer);
56/// assert_eq!(plain_text, "hello");
57/// ```
58pub trait Renderer {
59 /// Render one AST node into this renderer's output.
60 fn render_node(&mut self, node: &Node) -> String;
61
62 /// Render a node and its descendants.
63 fn render(&mut self, node: &Node) -> String {
64 self.render_node(node)
65 }
66
67 /// Render the direct children of a parent node in order.
68 fn render_children(&mut self, node: &Node) -> String {
69 let mut output = String::new();
70
71 if let Some(children) = node.children() {
72 for child in children {
73 output.push_str(&self.render(child));
74 }
75 }
76
77 output
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::Renderer;
84 use crate::mdast::{Node, Root, Text};
85 use alloc::string::String;
86 use alloc::vec;
87
88 struct PlainTextRenderer;
89
90 impl Renderer for PlainTextRenderer {
91 fn render_node(&mut self, node: &Node) -> String {
92 match node {
93 Node::Text(text) => text.value.clone(),
94 _ => self.render_children(node),
95 }
96 }
97 }
98
99 #[test]
100 fn renders_nodes_and_their_children_by_default() {
101 let node = Node::Root(Root {
102 position: None,
103 children: vec![
104 Node::Text(Text {
105 value: "one".into(),
106 position: None,
107 }),
108 Node::Text(Text {
109 value: "two".into(),
110 position: None,
111 }),
112 ],
113 });
114 let mut renderer = PlainTextRenderer;
115
116 assert_eq!(renderer.render(&node), "onetwo");
117 assert_eq!(
118 renderer.render_children(&Node::ThematicBreak(crate::mdast::ThematicBreak {
119 position: None,
120 })),
121 ""
122 );
123 }
124}