1use cmark_writer::CommonMarkWriter;
4use cmark_writer::HtmlAttribute;
5use cmark_writer::HtmlElement;
6use cmark_writer::HtmlWriteResult;
7use cmark_writer::HtmlWriter;
8use cmark_writer::HtmlWriterOptions;
9use cmark_writer::WriteResult;
10use cmark_writer::WriterOptions;
11use cmark_writer::ast::Node;
12use cmark_writer::custom_node;
13use ecow::EcoString;
14use ecow::eco_format;
15use std::path::PathBuf;
16
17use crate::Result;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum ListState {
21 Ordered,
22 Unordered,
23}
24
25#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
27pub enum Format {
28 #[default]
29 Md,
30 LaTeX,
31 Text,
32 #[cfg(feature = "docx")]
33 Docx,
34}
35
36#[derive(Debug, PartialEq, Clone)]
38#[custom_node(block = true, html_impl = true)]
39pub struct FigureNode {
40 pub body: Box<Node>,
42 pub caption: String,
44}
45
46impl FigureNode {
47 fn write_custom(&self, writer: &mut CommonMarkWriter) -> WriteResult<()> {
48 let mut temp_writer = CommonMarkWriter::with_options(writer.options.clone());
49 temp_writer.write(&self.body)?;
50 let content = temp_writer.into_string();
51 writer.write_str(&content)?;
52 writer.write_str("\n")?;
53 writer.write_str(&self.caption)?;
54 Ok(())
55 }
56
57 fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
58 let body = self.body.clone();
59 let node = Node::HtmlElement(HtmlElement {
60 tag: EcoString::inline("figure"),
61 attributes: vec![HtmlAttribute {
62 name: EcoString::inline("class"),
63 value: EcoString::inline("figure"),
64 }],
65 children: vec![*body],
66 self_closing: false,
67 });
68 writer.write_node(&node)?;
69 Ok(())
70 }
71}
72
73#[derive(Debug, PartialEq, Clone)]
75#[custom_node(block = true, html_impl = true)]
76pub struct ExternalFrameNode {
77 pub file_path: PathBuf,
79 pub alt_text: EcoString,
81 pub svg: String,
83}
84
85impl ExternalFrameNode {
86 fn write_custom(&self, writer: &mut CommonMarkWriter) -> WriteResult<()> {
87 writer.write_str(&format!(
89 "",
90 self.alt_text,
91 self.file_path.display()
92 ))?;
93 Ok(())
94 }
95
96 fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
97 let node = Node::HtmlElement(HtmlElement {
98 tag: EcoString::inline("img"),
99 attributes: vec![
100 HtmlAttribute {
101 name: EcoString::inline("src"),
102 value: self.file_path.display().to_string().into(),
103 },
104 HtmlAttribute {
105 name: EcoString::inline("alt"),
106 value: self.alt_text.clone(),
107 },
108 ],
109 children: vec![],
110 self_closing: true,
111 });
112 writer.write_node(&node)?;
113 Ok(())
114 }
115}
116
117#[derive(Debug, PartialEq, Clone)]
119#[custom_node(block = false, html_impl = true)]
120pub struct HighlightNode {
121 pub content: Vec<Node>,
123}
124
125impl HighlightNode {
126 fn write_custom(&self, writer: &mut CommonMarkWriter) -> WriteResult<()> {
127 let mut temp_writer = CommonMarkWriter::with_options(writer.options.clone());
128 for node in &self.content {
129 temp_writer.write(node)?;
130 }
131 let content = temp_writer.into_string();
132 writer.write_str(&format!("=={content}=="))?;
133 Ok(())
134 }
135
136 fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
137 let node = Node::HtmlElement(HtmlElement {
138 tag: EcoString::inline("mark"),
139 attributes: vec![],
140 children: self.content.clone(),
141 self_closing: false,
142 });
143 writer.write_node(&node)?;
144 Ok(())
145 }
146}
147
148#[derive(Debug, PartialEq, Clone)]
150#[custom_node(block = true, html_impl = true)]
151pub struct CenterNode {
152 pub node: Node,
154}
155
156impl CenterNode {
157 pub fn new(children: Vec<Node>) -> Self {
158 CenterNode {
159 node: Node::HtmlElement(cmark_writer::ast::HtmlElement {
160 tag: EcoString::inline("p"),
161 attributes: vec![cmark_writer::ast::HtmlAttribute {
162 name: EcoString::inline("align"),
163 value: EcoString::inline("center"),
164 }],
165 children,
166 self_closing: false,
167 }),
168 }
169 }
170
171 fn write_custom(&self, writer: &mut CommonMarkWriter) -> WriteResult<()> {
172 let mut temp_writer = CommonMarkWriter::with_options(writer.options.clone());
173 temp_writer.write(&self.node)?;
174 let content = temp_writer.into_string();
175 writer.write_str(&content)?;
176 writer.write_str("\n")?;
177 Ok(())
178 }
179
180 fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
181 let mut temp_writer = HtmlWriter::with_options(HtmlWriterOptions {
182 strict: false,
183 ..Default::default()
184 });
185 temp_writer.write_node(&self.node)?;
186 let content = temp_writer.into_string();
187 writer.write_str(&content)?;
188 Ok(())
189 }
190}
191
192#[derive(Debug, PartialEq, Clone)]
194#[custom_node(block = false, html_impl = true)]
195pub struct InlineNode {
196 pub content: Vec<Node>,
198}
199
200impl InlineNode {
201 fn write_custom(&self, writer: &mut CommonMarkWriter) -> WriteResult<()> {
202 for node in &self.content {
203 writer.write(node)?;
204 }
205 Ok(())
206 }
207
208 fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
209 for node in &self.content {
210 writer.write_node(node)?;
211 }
212 Ok(())
213 }
214}
215
216#[derive(Debug, PartialEq, Clone)]
218#[custom_node(block = false, html_impl = false)]
219pub struct VerbatimNode {
220 pub content: EcoString,
222}
223
224impl VerbatimNode {
225 fn write_custom(&self, writer: &mut CommonMarkWriter) -> WriteResult<()> {
226 writer.write_str(&self.content)?;
227 Ok(())
228 }
229}
230
231#[derive(Debug, PartialEq, Clone)]
233#[custom_node(block = true, html_impl = false)]
234pub struct AlertNode {
235 pub content: Vec<Node>,
237 pub class: EcoString,
239}
240
241impl AlertNode {
242 fn write_custom(&self, writer: &mut CommonMarkWriter) -> WriteResult<()> {
243 let quote = Node::BlockQuote(vec![
244 Node::Paragraph(vec![Node::Text(eco_format!(
245 "[!{}]",
246 self.class.to_ascii_uppercase()
247 ))]),
248 Node::Paragraph(vec![Node::Text("".into())]),
249 ]);
250 let mut tmp_writer = CommonMarkWriter::with_options(WriterOptions {
251 escape_special_chars: false,
252 ..writer.options.clone()
253 });
254 tmp_writer.write("e)?;
255 let mut content = tmp_writer.into_string();
256 let quote = Node::BlockQuote(self.content.clone());
257 let mut tmp_writer = CommonMarkWriter::with_options(writer.options.clone());
258 tmp_writer.write("e)?;
259 content += tmp_writer.into_string();
260 writer.write_str(&content)?;
261 Ok(())
262 }
263}
264
265pub trait FormatWriter {
267 fn write_eco(&mut self, document: &Node, output: &mut EcoString) -> Result<()>;
269
270 fn write_vec(&mut self, document: &Node) -> Result<Vec<u8>>;
272}