cmark_writer/writer/runtime/visitor/
mod.rs1use crate::ast::{CodeBlockType, CustomNode, HeadingType, HtmlElement, ListItem, Node};
9use ecow::EcoString;
10
11#[cfg(feature = "gfm")]
12use crate::ast::TableAlignment;
13
14#[allow(missing_docs)]
16pub trait NodeHandler {
17 type Error;
19
20 fn visit_node(&mut self, node: &Node) -> Result<(), Self::Error> {
23 walk_node(self, node)
24 }
25
26 fn visit_nodes(&mut self, nodes: &[Node]) -> Result<(), Self::Error> {
28 for node in nodes {
29 self.visit_node(node)?;
30 }
31 Ok(())
32 }
33
34 fn document(&mut self, children: &[Node]) -> Result<(), Self::Error> {
35 self.visit_nodes(children)
36 }
37
38 fn paragraph(&mut self, content: &[Node]) -> Result<(), Self::Error> {
39 self.visit_nodes(content)
40 }
41
42 fn text(&mut self, _text: &EcoString) -> Result<(), Self::Error> {
43 Ok(())
44 }
45
46 fn emphasis(&mut self, content: &[Node]) -> Result<(), Self::Error> {
47 self.visit_nodes(content)
48 }
49
50 fn strong(&mut self, content: &[Node]) -> Result<(), Self::Error> {
51 self.visit_nodes(content)
52 }
53
54 fn thematic_break(&mut self) -> Result<(), Self::Error> {
55 Ok(())
56 }
57
58 fn heading(
59 &mut self,
60 _level: u8,
61 content: &[Node],
62 _heading_type: &HeadingType,
63 ) -> Result<(), Self::Error> {
64 self.visit_nodes(content)
65 }
66
67 fn inline_code(&mut self, _code: &EcoString) -> Result<(), Self::Error> {
68 Ok(())
69 }
70
71 fn code_block(
72 &mut self,
73 _language: &Option<EcoString>,
74 _content: &EcoString,
75 _kind: &CodeBlockType,
76 ) -> Result<(), Self::Error> {
77 Ok(())
78 }
79
80 fn html_block(&mut self, _content: &EcoString) -> Result<(), Self::Error> {
81 Ok(())
82 }
83
84 fn html_element(&mut self, _element: &HtmlElement) -> Result<(), Self::Error> {
85 Ok(())
86 }
87
88 fn block_quote(&mut self, content: &[Node]) -> Result<(), Self::Error> {
89 self.visit_nodes(content)
90 }
91
92 fn unordered_list(&mut self, _items: &[ListItem]) -> Result<(), Self::Error> {
93 Ok(())
94 }
95
96 fn ordered_list(&mut self, _start: u32, _items: &[ListItem]) -> Result<(), Self::Error> {
97 Ok(())
98 }
99
100 #[cfg(feature = "gfm")]
101 fn table(
102 &mut self,
103 _headers: &[Node],
104 _alignments: &[TableAlignment],
105 _rows: &[Vec<Node>],
106 ) -> Result<(), Self::Error> {
107 Ok(())
108 }
109
110 #[cfg(not(feature = "gfm"))]
111 fn table(&mut self, _headers: &[Node], _rows: &[Vec<Node>]) -> Result<(), Self::Error> {
112 Ok(())
113 }
114
115 fn link(
116 &mut self,
117 _url: &EcoString,
118 _title: &Option<EcoString>,
119 content: &[Node],
120 ) -> Result<(), Self::Error> {
121 self.visit_nodes(content)
122 }
123
124 fn image(
125 &mut self,
126 _url: &EcoString,
127 _title: &Option<EcoString>,
128 _alt: &[Node],
129 ) -> Result<(), Self::Error> {
130 Ok(())
131 }
132
133 fn soft_break(&mut self) -> Result<(), Self::Error> {
134 Ok(())
135 }
136
137 fn hard_break(&mut self) -> Result<(), Self::Error> {
138 Ok(())
139 }
140
141 fn autolink(&mut self, _url: &EcoString, _is_email: bool) -> Result<(), Self::Error> {
142 Ok(())
143 }
144
145 #[cfg(feature = "gfm")]
146 fn extended_autolink(&mut self, _url: &EcoString) -> Result<(), Self::Error> {
147 Ok(())
148 }
149
150 fn link_reference_definition(
151 &mut self,
152 _label: &EcoString,
153 _destination: &EcoString,
154 _title: &Option<EcoString>,
155 ) -> Result<(), Self::Error> {
156 Ok(())
157 }
158
159 fn reference_link(&mut self, _label: &EcoString, content: &[Node]) -> Result<(), Self::Error> {
160 self.visit_nodes(content)
161 }
162
163 #[cfg(feature = "gfm")]
164 fn strikethrough(&mut self, content: &[Node]) -> Result<(), Self::Error> {
165 self.visit_nodes(content)
166 }
167
168 fn custom(&mut self, _node: &dyn CustomNode) -> Result<(), Self::Error> {
169 Ok(())
170 }
171
172 fn unsupported(&mut self, _node: &Node) -> Result<(), Self::Error> {
173 Ok(())
174 }
175}
176
177pub fn walk_node<H: NodeHandler + ?Sized>(handler: &mut H, node: &Node) -> Result<(), H::Error> {
179 match node {
180 Node::Document(children) => handler.document(children),
181 Node::Paragraph(content) => handler.paragraph(content),
182 Node::Text(text) => handler.text(text),
183 Node::Emphasis(content) => handler.emphasis(content),
184 Node::Strong(content) => handler.strong(content),
185 Node::ThematicBreak => handler.thematic_break(),
186 Node::Heading {
187 level,
188 content,
189 heading_type,
190 } => handler.heading(*level, content, heading_type),
191 Node::InlineCode(code) => handler.inline_code(code),
192 Node::CodeBlock {
193 language,
194 content,
195 block_type,
196 } => handler.code_block(language, content, block_type),
197 Node::HtmlBlock(content) => handler.html_block(content),
198 Node::HtmlElement(element) => handler.html_element(element),
199 Node::BlockQuote(content) => handler.block_quote(content),
200 Node::UnorderedList(items) => handler.unordered_list(items),
201 Node::OrderedList { start, items } => handler.ordered_list(*start, items),
202 Node::Link {
203 url,
204 title,
205 content,
206 } => handler.link(url, title, content),
207 Node::Image { url, title, alt } => handler.image(url, title, alt),
208 Node::Autolink { url, is_email } => handler.autolink(url, *is_email),
209 Node::SoftBreak => handler.soft_break(),
210 Node::HardBreak => handler.hard_break(),
211 Node::LinkReferenceDefinition {
212 label,
213 destination,
214 title,
215 } => handler.link_reference_definition(label, destination, title),
216 Node::ReferenceLink { label, content } => handler.reference_link(label, content),
217 Node::Custom(custom_node) => handler.custom(custom_node.as_ref()),
218 #[cfg(feature = "gfm")]
219 Node::Table {
220 headers,
221 alignments,
222 rows,
223 } => handler.table(headers, alignments, rows),
224 #[cfg(not(feature = "gfm"))]
225 Node::Table { headers, rows, .. } => handler.table(headers, rows),
226 #[cfg(feature = "gfm")]
227 Node::Strikethrough(content) => handler.strikethrough(content),
228 #[cfg(feature = "gfm")]
229 Node::ExtendedAutolink(url) => handler.extended_autolink(url),
230 #[allow(unreachable_patterns)]
231 _ => handler.unsupported(node),
232 }
233}