cmark_writer/ast.rs
1//! Abstract Syntax Tree for CommonMark document structure.
2//!
3//! This module defines various node types for representing CommonMark documents,
4//! including headings, paragraphs, lists, code blocks, etc.
5
6/// Main node type, representing an element in a CommonMark document
7#[derive(Debug, Clone, PartialEq)]
8pub enum Node {
9 // Block-level nodes
10 /// Root document node, contains child nodes
11 Document(Vec<Node>),
12
13 /// Heading, contains level (1-6) and inline content
14 Heading {
15 /// Heading level, 1-6
16 level: u8,
17 /// Heading content, containing inline elements
18 content: Vec<Node>,
19 },
20
21 /// Paragraph node, containing inline elements
22 Paragraph(Vec<Node>),
23
24 /// Block quote, containing any block-level elements
25 BlockQuote(Vec<Node>),
26
27 /// Code block, containing optional language identifier and content
28 CodeBlock {
29 /// Optional language identifier
30 language: Option<String>,
31 /// Code content
32 content: String,
33 },
34
35 /// Unordered list, containing list items
36 UnorderedList(Vec<ListItem>),
37
38 /// Ordered list, containing starting number and list items
39 OrderedList {
40 /// List starting number
41 start: u32,
42 /// List items
43 items: Vec<ListItem>,
44 },
45
46 /// Thematic break (horizontal rule)
47 ThematicBreak,
48
49 /// Table
50 Table {
51 /// Header cells
52 headers: Vec<Node>,
53 /// Table rows, each row containing multiple cells
54 rows: Vec<Vec<Node>>,
55 /// Column alignments
56 alignments: Vec<Alignment>,
57 },
58
59 /// HTML block
60 HtmlBlock(String),
61
62 // Inline nodes
63 /// Plain text
64 Text(String),
65
66 /// Emphasis (italic)
67 Emphasis(Vec<Node>),
68
69 /// Strong emphasis (bold)
70 Strong(Vec<Node>),
71
72 /// Strikethrough
73 Strike(Vec<Node>),
74
75 /// Inline code
76 InlineCode(String),
77
78 /// Link
79 Link {
80 /// Link URL
81 url: String,
82 /// Optional link title
83 title: Option<String>,
84 /// Link text
85 content: Vec<Node>,
86 },
87
88 /// Image
89 Image {
90 /// Image URL
91 url: String,
92 /// Optional image title
93 title: Option<String>,
94 /// Alternative text, containing inline elements
95 alt: Vec<Node>,
96 },
97
98 /// Inline element collection, without formatting and line breaks
99 InlineContainer(Vec<Node>),
100
101 /// HTML inline element
102 HtmlElement(HtmlElement),
103
104 /// Soft break (single line break)
105 SoftBreak,
106
107 /// Hard break (two spaces followed by a line break, or backslash followed by a line break)
108 HardBreak,
109}
110
111/// List item type
112#[derive(Debug, Clone, PartialEq)]
113pub enum ListItem {
114 /// Unordered list item
115 Unordered {
116 /// List item content, containing one or more block-level elements
117 content: Vec<Node>,
118 },
119 /// Ordered list item
120 Ordered {
121 /// Optional item number for ordered lists, allowing manual numbering
122 number: Option<u32>,
123 /// List item content, containing one or more block-level elements
124 content: Vec<Node>,
125 },
126}
127
128/// Table column alignment
129#[derive(Debug, Clone, Copy, PartialEq)]
130pub enum Alignment {
131 /// No specific alignment
132 None,
133 /// Left alignment
134 Left,
135 /// Center alignment
136 Center,
137 /// Right alignment
138 Right,
139}
140
141/// Represents an HTML attribute, containing name and value
142#[derive(Debug, Clone, PartialEq)]
143pub struct HtmlAttribute {
144 /// Attribute name
145 pub name: String,
146 /// Attribute value
147 pub value: String,
148}
149
150/// Represents an HTML element, containing tag name, attributes, and child nodes
151#[derive(Debug, Clone, PartialEq)]
152pub struct HtmlElement {
153 /// Element tag name
154 pub tag: String,
155 /// Element attributes
156 pub attributes: Vec<HtmlAttribute>,
157 /// Element child nodes (can only contain inline nodes)
158 pub children: Vec<Node>,
159 /// Whether it's a self-closing tag (e.g. <img />)
160 pub self_closing: bool,
161}
162
163impl Node {
164 /// Check if a node is a block-level node
165 pub fn is_block(&self) -> bool {
166 matches!(
167 self,
168 Node::Document(_)
169 | Node::Heading { .. }
170 | Node::Paragraph(_)
171 | Node::BlockQuote(_)
172 | Node::CodeBlock { .. }
173 | Node::UnorderedList(_)
174 | Node::OrderedList { .. }
175 | Node::ThematicBreak
176 | Node::Table { .. }
177 | Node::HtmlBlock(_)
178 )
179 }
180
181 /// Check if a node is an inline node
182 pub fn is_inline(&self) -> bool {
183 matches!(
184 self,
185 Node::Text(_)
186 | Node::Emphasis(_)
187 | Node::Strong(_)
188 | Node::Strike(_)
189 | Node::InlineCode(_)
190 | Node::Link { .. }
191 | Node::Image { .. }
192 | Node::InlineContainer(_)
193 | Node::HtmlElement(_)
194 | Node::SoftBreak
195 | Node::HardBreak
196 )
197 }
198}