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