1use cmark_writer::HtmlAttribute;
4use cmark_writer::HtmlElement;
5use cmark_writer::HtmlWriteResult;
6use cmark_writer::HtmlWriter;
7use cmark_writer::HtmlWriterOptions;
8use cmark_writer::WriteResult;
9use cmark_writer::ast::Node;
10use cmark_writer::custom_node;
11use cmark_writer::writer::{BlockWriterProxy, InlineWriterProxy};
12use ecow::EcoString;
13use ecow::eco_format;
14use std::path::PathBuf;
15
16use crate::Result;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum ListState {
20 Ordered,
21 Unordered,
22}
23
24#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
26pub enum Format {
27 #[default]
28 Md,
29 LaTeX,
30 Text,
31 #[cfg(feature = "docx")]
32 Docx,
33}
34
35#[derive(Debug, PartialEq, Clone)]
37#[custom_node(block = true, html_impl = true)]
38pub struct FigureNode {
39 pub body: Box<Node>,
41 pub caption: String,
43}
44
45impl FigureNode {
46 fn write_custom(&self, writer: &mut BlockWriterProxy) -> WriteResult<()> {
47 let content = writer.capture_block(|block| {
48 block.write_block(&self.body)?;
49 Ok(())
50 })?;
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 BlockWriterProxy) -> 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 InlineWriterProxy) -> WriteResult<()> {
127 writer.write_str("==")?;
128 writer.write_inline_nodes(&self.content)?;
129 writer.write_str("==")?;
130 Ok(())
131 }
132
133 fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
134 let node = Node::HtmlElement(HtmlElement {
135 tag: EcoString::inline("mark"),
136 attributes: vec![],
137 children: self.content.clone(),
138 self_closing: false,
139 });
140 writer.write_node(&node)?;
141 Ok(())
142 }
143}
144
145#[derive(Debug, PartialEq, Clone)]
147#[custom_node(block = true, html_impl = true)]
148pub struct CenterNode {
149 pub node: Node,
151}
152
153impl CenterNode {
154 pub fn new(children: Vec<Node>) -> Self {
155 CenterNode {
156 node: Node::HtmlElement(cmark_writer::ast::HtmlElement {
157 tag: EcoString::inline("p"),
158 attributes: vec![cmark_writer::ast::HtmlAttribute {
159 name: EcoString::inline("align"),
160 value: EcoString::inline("center"),
161 }],
162 children,
163 self_closing: false,
164 }),
165 }
166 }
167
168 fn write_custom(&self, writer: &mut BlockWriterProxy) -> WriteResult<()> {
169 let content = writer.capture_inline(|inline| {
170 inline.write_inline(&self.node)?;
171 Ok(())
172 })?;
173 writer.write_str(&content)?;
174 writer.write_str("\n")?;
175 Ok(())
176 }
177
178 fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
179 let mut temp_writer = HtmlWriter::with_options(HtmlWriterOptions {
180 strict: false,
181 ..Default::default()
182 });
183 temp_writer.write_node(&self.node)?;
184 let content = temp_writer.into_string()?;
185 writer.write_trusted_html(&content)?;
186 Ok(())
187 }
188}
189
190#[derive(Debug, PartialEq, Clone)]
192#[custom_node(block = false, html_impl = true)]
193pub struct InlineNode {
194 pub content: Vec<Node>,
196}
197
198impl InlineNode {
199 fn write_custom(&self, writer: &mut InlineWriterProxy) -> WriteResult<()> {
200 writer.write_inline_nodes(&self.content)
201 }
202
203 fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
204 for node in &self.content {
205 writer.write_node(node)?;
206 }
207 Ok(())
208 }
209}
210
211#[derive(Debug, PartialEq, Clone)]
213#[custom_node(block = false, html_impl = true)]
214pub struct VerbatimNode {
215 pub content: EcoString,
217}
218
219impl VerbatimNode {
220 fn write_custom(&self, writer: &mut InlineWriterProxy) -> WriteResult<()> {
221 writer.write_str(&self.content)
222 }
223
224 fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
225 writer.write_trusted_html(&self.content)
226 }
227}
228
229#[derive(Debug, PartialEq, Clone)]
231#[custom_node(block = true, html_impl = false)]
232pub struct AlertNode {
233 pub content: Vec<Node>,
235 pub class: EcoString,
237}
238
239impl AlertNode {
240 fn write_custom(&self, writer: &mut BlockWriterProxy) -> WriteResult<()> {
241 let quote = Node::BlockQuote(vec![
242 Node::Paragraph(vec![Node::Text(eco_format!(
243 "[!{}]",
244 self.class.to_ascii_uppercase()
245 ))]),
246 Node::Paragraph(vec![Node::Text("".into())]),
247 ]);
248 writer.with_temporary_options(
249 |options| options.escape_special_chars = false,
250 |writer| writer.write_block("e),
251 )?;
252 let quote = Node::BlockQuote(self.content.clone());
253 writer.write_block("e)?;
254 Ok(())
255 }
256}
257
258pub trait FormatWriter {
260 fn write_eco(&mut self, document: &Node, output: &mut EcoString) -> Result<()>;
262
263 fn write_vec(&mut self, document: &Node) -> Result<Vec<u8>>;
265}