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}