cmark_writer/ast/node.rs
1//! Node definitions for the CommonMark AST.
2
3use super::custom::CustomNode;
4use super::html::HtmlElement;
5use std::boxed::Box;
6
7/// Code block type according to CommonMark specification
8#[derive(Debug, Clone, PartialEq, Default)]
9pub enum CodeBlockType {
10 /// Indented code block - composed of one or more indented chunks, each preceded by four or more spaces
11 Indented,
12 /// Fenced code block - surrounded by backtick or tilde fences
13 #[default]
14 Fenced,
15}
16
17/// Heading type according to CommonMark specification
18#[derive(Debug, Clone, PartialEq, Default)]
19pub enum HeadingType {
20 /// ATX Type - Beginning with #
21 #[default]
22 Atx,
23 /// Setext Type - Underlined or overlined text
24 Setext,
25}
26
27/// Main node type, representing an element in a CommonMark document
28#[derive(Debug, Clone, PartialEq)]
29pub enum Node {
30 /// Root document node, contains child nodes
31 Document(Vec<Node>),
32
33 // Leaf blocks
34 // Thematic breaks
35 /// Thematic break (horizontal rule)
36 ThematicBreak,
37
38 // ATX headings & Setext headings
39 /// Heading, contains level (1-6) and inline content
40 Heading {
41 /// Heading level, 1-6
42 level: u8,
43 /// Heading content, containing inline elements
44 content: Vec<Node>,
45 /// Heading type (ATX or Setext)
46 heading_type: HeadingType,
47 },
48
49 // Indented code blocks & Fenced code blocks
50 /// Code block, containing optional language identifier and content
51 CodeBlock {
52 /// Optional language identifier (None for indented code blocks, Some for fenced code blocks)
53 language: Option<String>,
54 /// Code content
55 content: String,
56 /// The type of code block (Indented or Fenced)
57 block_type: CodeBlockType,
58 },
59
60 // HTML blocks
61 /// HTML block
62 HtmlBlock(String),
63
64 // Link reference definitions
65 /// Link reference definition
66 LinkReferenceDefinition {
67 /// Link label (used for reference)
68 label: String,
69 /// Link destination URL
70 destination: String,
71 /// Optional link title
72 title: Option<String>,
73 },
74
75 // Paragraphs
76 /// Paragraph node, containing inline elements
77 Paragraph(Vec<Node>),
78
79 // Blank lines - typically handled during parsing, not represented in AST
80
81 // Container blocks
82 // Block quotes
83 /// Block quote, containing any block-level elements
84 BlockQuote(Vec<Node>),
85
86 // & List items and Lists
87 /// Ordered list, containing starting number and list items
88 OrderedList {
89 /// List starting number
90 start: u32,
91 /// List items
92 items: Vec<ListItem>,
93 },
94
95 /// Unordered list, containing list items
96 UnorderedList(Vec<ListItem>),
97
98 /// Table (extension to CommonMark)
99 Table {
100 /// Header cells
101 headers: Vec<Node>,
102 /// Table rows, each row containing multiple cells
103 rows: Vec<Vec<Node>>,
104 },
105
106 // Inlines
107 // Code spans
108 /// Inline code
109 InlineCode(String),
110
111 // Emphasis and strong emphasis
112 /// Emphasis (italic)
113 Emphasis(Vec<Node>),
114
115 /// Strong emphasis (bold)
116 Strong(Vec<Node>),
117
118 // Links
119 /// Link
120 Link {
121 /// Link URL
122 url: String,
123 /// Optional link title
124 title: Option<String>,
125 /// Link text
126 content: Vec<Node>,
127 },
128
129 /// Reference link
130 ReferenceLink {
131 /// Link reference label
132 label: String,
133 /// Link text content (optional, if empty it's a shortcut reference)
134 content: Vec<Node>,
135 },
136
137 // Images
138 /// Image
139 Image {
140 /// Image URL
141 url: String,
142 /// Optional image title
143 title: Option<String>,
144 /// Alternative text, containing inline elements
145 alt: Vec<Node>,
146 },
147
148 // Autolinks
149 /// Autolink (URI or email wrapped in < and >)
150 Autolink {
151 /// Link URL
152 url: String,
153 /// Whether this is an email autolink
154 is_email: bool,
155 },
156
157 // Raw HTML
158 /// HTML inline element
159 HtmlElement(HtmlElement),
160
161 // Hard line breaks
162 /// Hard break (two spaces followed by a line break, or backslash followed by a line break)
163 HardBreak,
164
165 // Soft line breaks
166 /// Soft break (single line break)
167 SoftBreak,
168
169 // Textual content
170 /// Plain text
171 Text(String),
172
173 /// Custom node that allows users to implement their own writing behavior
174 Custom(Box<dyn CustomNode>),
175}
176
177impl Default for Node {
178 fn default() -> Self {
179 Node::Document(vec![])
180 }
181}
182
183/// List item type
184#[derive(Debug, Clone, PartialEq)]
185pub enum ListItem {
186 /// Unordered list item
187 Unordered {
188 /// List item content, containing one or more block-level elements
189 content: Vec<Node>,
190 },
191 /// Ordered list item
192 Ordered {
193 /// Optional item number for ordered lists, allowing manual numbering
194 number: Option<u32>,
195 /// List item content, containing one or more block-level elements
196 content: Vec<Node>,
197 },
198}
199
200impl Node {
201 /// Check if a node is a block-level node
202 pub fn is_block(&self) -> bool {
203 matches!(
204 self,
205 Node::Document(_)
206 // Leaf blocks
207 | Node::ThematicBreak
208 | Node::Heading { .. }
209 | Node::CodeBlock { .. }
210 | Node::HtmlBlock(_)
211 | Node::LinkReferenceDefinition { .. }
212 | Node::Paragraph(_)
213 // Container blocks
214 | Node::BlockQuote(_)
215 | Node::OrderedList { .. }
216 | Node::UnorderedList(_)
217 | Node::Table { .. }
218
219 | Node::Custom(_)
220 )
221 }
222
223 /// Check if a node is an inline node
224 pub fn is_inline(&self) -> bool {
225 matches!(
226 self,
227 // Inlines
228 // Code spans
229 Node::InlineCode(_)
230 // Emphasis and strong emphasis
231 | Node::Emphasis(_)
232 | Node::Strong(_)
233 // Links
234 | Node::Link { .. }
235 | Node::ReferenceLink { .. }
236 // Images
237 | Node::Image { .. }
238 // Autolinks
239 | Node::Autolink { .. }
240 // Raw HTML
241 | Node::HtmlElement(_)
242 // Hard line breaks
243 | Node::HardBreak
244 // Soft line breaks
245 | Node::SoftBreak
246 // Textual content
247 | Node::Text(_)
248
249 | Node::Custom(_)
250 )
251 }
252 /// Create a heading node
253 ///
254 /// # Arguments
255 /// * `level` - Heading level (1-6)
256 /// * `content` - Heading content
257 ///
258 /// # Returns
259 /// A new heading node, default ATX type
260 pub fn heading(level: u8, content: Vec<Node>) -> Self {
261 Node::Heading {
262 level,
263 content,
264 heading_type: HeadingType::default(),
265 }
266 }
267
268 /// Create a code block node
269 ///
270 /// # Arguments
271 /// * `language` - Optional language identifier
272 /// * `content` - Code content
273 ///
274 /// # Returns
275 /// A new code block node, default Fenced type
276 pub fn code_block(language: Option<String>, content: String) -> Self {
277 Node::CodeBlock {
278 language,
279 content,
280 block_type: CodeBlockType::default(),
281 }
282 }
283}