cmark_writer/writer/cmark/
core.rs1#[cfg(feature = "gfm")]
2use crate::ast::TableAlignment;
3use crate::ast::{CodeBlockType, CustomNode, HeadingType, ListItem, Node};
4use crate::error::{WriteError, WriteResult};
5use crate::options::WriterOptions;
6use crate::writer::runtime::diagnostics::{Diagnostic, DiagnosticSink, NullSink};
7use crate::writer::runtime::proxy::{BlockWriterProxy, InlineWriterProxy};
8use crate::writer::runtime::visitor::{walk_node, NodeHandler};
9use ecow::EcoString;
10use log;
11use std::fmt;
12
13use super::format::FormatPolicy;
14use super::utils::node_contains_newline;
15
16pub struct CommonMarkWriter {
18 pub options: WriterOptions,
20 pub buffer: EcoString,
22 format: FormatPolicy,
24 diagnostics: Box<dyn DiagnosticSink + 'static>,
26}
27
28impl fmt::Debug for CommonMarkWriter {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 f.debug_struct("CommonMarkWriter")
31 .field("options", &self.options)
32 .field("buffer", &self.buffer)
33 .field("format", &self.format)
34 .finish()
35 }
36}
37
38impl CommonMarkWriter {
39 pub fn new() -> Self {
41 Self::with_options(WriterOptions::default())
42 }
43
44 pub fn with_options(options: WriterOptions) -> Self {
46 Self {
47 options,
48 buffer: EcoString::new(),
49 format: FormatPolicy,
50 diagnostics: Box::new(NullSink),
51 }
52 }
53
54 pub fn with_diagnostic_sink(mut self, sink: Box<dyn DiagnosticSink + 'static>) -> Self {
56 self.diagnostics = sink;
57 self
58 }
59
60 pub fn set_diagnostic_sink(&mut self, sink: Box<dyn DiagnosticSink + 'static>) {
62 self.diagnostics = sink;
63 }
64
65 pub fn diagnostic_sink(&mut self) -> &mut dyn DiagnosticSink {
67 self.diagnostics.as_mut()
68 }
69
70 pub(crate) fn is_strict_mode(&self) -> bool {
72 self.options.strict
73 }
74
75 pub fn write(&mut self, node: &Node) -> WriteResult<()> {
77 walk_node(self, node)
78 }
79
80 pub(crate) fn write_custom_node(&mut self, node: &dyn CustomNode) -> WriteResult<()> {
82 if node.is_block() {
83 let mut proxy = BlockWriterProxy::new(self);
84 node.write_block(&mut proxy)
85 } else {
86 let mut proxy = InlineWriterProxy::new(self);
87 node.write_inline(&mut proxy)
88 }
89 }
90
91 pub(crate) fn check_no_newline(&mut self, node: &Node, context: &str) -> WriteResult<()> {
93 if node_contains_newline(node) {
94 if self.is_strict_mode() {
95 return Err(WriteError::NewlineInInlineElement(
96 context.to_string().into(),
97 ));
98 } else {
99 self.emit_warning(format!(
100 "Newline character found in inline element '{context}', but non-strict mode allows it (output may be affected)."
101 ));
102 }
103 }
104 Ok(())
105 }
106
107 pub(crate) fn capture_with_buffer<F>(&mut self, f: F) -> WriteResult<EcoString>
109 where
110 F: FnOnce(&mut Self) -> WriteResult<()>,
111 {
112 let mut outer = EcoString::new();
113 std::mem::swap(&mut self.buffer, &mut outer);
114
115 let write_result = f(self);
116 let captured = std::mem::take(&mut self.buffer);
117 std::mem::swap(&mut self.buffer, &mut outer);
118
119 write_result?;
120 Ok(captured)
121 }
122
123 pub fn into_string(self) -> EcoString {
125 self.buffer
126 }
127
128 pub fn write_str(&mut self, s: &str) -> WriteResult<()> {
130 self.buffer.push_str(s);
131 Ok(())
132 }
133
134 pub fn write_char(&mut self, c: char) -> WriteResult<()> {
136 self.buffer.push(c);
137 Ok(())
138 }
139
140 pub(crate) fn ensure_trailing_newline(&mut self) -> WriteResult<()> {
142 self.format.ensure_trailing_newline(&mut self.buffer)
143 }
144
145 pub(crate) fn ensure_blank_line(&mut self) -> WriteResult<()> {
147 self.format.ensure_blank_line(&mut self.buffer)
148 }
149
150 pub(crate) fn prepare_block_sequence(
152 &mut self,
153 previous_was_block: bool,
154 next_is_block: bool,
155 ) -> WriteResult<()> {
156 self.format
157 .prepare_block_sequence(&mut self.buffer, previous_was_block, next_is_block)
158 }
159
160 pub(crate) fn emit_warning<S: Into<EcoString>>(&mut self, message: S) {
162 let message = message.into();
163 self.diagnostics.emit(Diagnostic::warning(message.clone()));
164 log::warn!("{message}");
165 }
166
167 pub(crate) fn emit_info<S: Into<EcoString>>(&mut self, message: S) {
169 let message = message.into();
170 self.diagnostics.emit(Diagnostic::info(message.clone()));
171 log::info!("{message}");
172 }
173}
174
175impl NodeHandler for CommonMarkWriter {
176 type Error = WriteError;
177
178 fn document(&mut self, children: &[Node]) -> WriteResult<()> {
179 self.write_document(children)
180 }
181
182 fn paragraph(&mut self, content: &[Node]) -> WriteResult<()> {
183 self.write_paragraph(content)
184 }
185
186 fn text(&mut self, text: &EcoString) -> WriteResult<()> {
187 self.write_text_content(text)
188 }
189
190 fn emphasis(&mut self, content: &[Node]) -> WriteResult<()> {
191 self.write_emphasis(content)
192 }
193
194 fn strong(&mut self, content: &[Node]) -> WriteResult<()> {
195 self.write_strong(content)
196 }
197
198 fn thematic_break(&mut self) -> WriteResult<()> {
199 self.write_thematic_break()
200 }
201
202 fn heading(
203 &mut self,
204 level: u8,
205 content: &[Node],
206 heading_type: &HeadingType,
207 ) -> WriteResult<()> {
208 self.write_heading(level, content, heading_type)
209 }
210
211 fn inline_code(&mut self, code: &EcoString) -> WriteResult<()> {
212 self.write_code_content(code)
213 }
214
215 fn code_block(
216 &mut self,
217 language: &Option<EcoString>,
218 content: &EcoString,
219 block_type: &CodeBlockType,
220 ) -> WriteResult<()> {
221 self.write_code_block(language, content, block_type)
222 }
223
224 fn html_block(&mut self, content: &EcoString) -> WriteResult<()> {
225 self.write_html_block(content)
226 }
227
228 fn html_element(&mut self, element: &crate::ast::HtmlElement) -> WriteResult<()> {
229 self.write_html_element(element)
230 }
231
232 fn block_quote(&mut self, content: &[Node]) -> WriteResult<()> {
233 self.write_blockquote(content)
234 }
235
236 fn unordered_list(&mut self, items: &[ListItem]) -> WriteResult<()> {
237 self.write_unordered_list(items)
238 }
239
240 fn ordered_list(&mut self, start: u32, items: &[ListItem]) -> WriteResult<()> {
241 self.write_ordered_list(start, items)
242 }
243
244 #[cfg(feature = "gfm")]
245 fn table(
246 &mut self,
247 headers: &[Node],
248 alignments: &[TableAlignment],
249 rows: &[Vec<Node>],
250 ) -> WriteResult<()> {
251 self.write_table_with_alignment(headers, alignments, rows)
252 }
253
254 #[cfg(not(feature = "gfm"))]
255 fn table(&mut self, headers: &[Node], rows: &[Vec<Node>]) -> WriteResult<()> {
256 self.write_table(headers, rows)
257 }
258
259 fn link(
260 &mut self,
261 url: &EcoString,
262 title: &Option<EcoString>,
263 content: &[Node],
264 ) -> WriteResult<()> {
265 self.write_link(url, title, content)
266 }
267
268 fn image(
269 &mut self,
270 url: &EcoString,
271 title: &Option<EcoString>,
272 alt: &[Node],
273 ) -> WriteResult<()> {
274 self.write_image(url, title, alt)
275 }
276
277 fn soft_break(&mut self) -> WriteResult<()> {
278 self.write_soft_break()
279 }
280
281 fn hard_break(&mut self) -> WriteResult<()> {
282 self.write_hard_break()
283 }
284
285 fn autolink(&mut self, url: &EcoString, is_email: bool) -> WriteResult<()> {
286 self.write_autolink(url, is_email)
287 }
288
289 #[cfg(feature = "gfm")]
290 fn extended_autolink(&mut self, url: &EcoString) -> WriteResult<()> {
291 self.write_extended_autolink(url)
292 }
293
294 fn link_reference_definition(
295 &mut self,
296 label: &EcoString,
297 destination: &EcoString,
298 title: &Option<EcoString>,
299 ) -> WriteResult<()> {
300 self.write_link_reference_definition(label, destination, title)
301 }
302
303 fn reference_link(&mut self, label: &EcoString, content: &[Node]) -> WriteResult<()> {
304 self.write_reference_link(label, content)
305 }
306
307 #[cfg(feature = "gfm")]
308 fn strikethrough(&mut self, content: &[Node]) -> WriteResult<()> {
309 self.write_strikethrough(content)
310 }
311
312 fn custom(&mut self, node: &dyn CustomNode) -> WriteResult<()> {
313 self.write_custom_node(node)
314 }
315
316 fn unsupported(&mut self, node: &Node) -> WriteResult<()> {
317 self.emit_warning(format!(
318 "Unsupported node type encountered and skipped: {node:?}"
319 ));
320 Ok(())
321 }
322}
323
324impl Default for CommonMarkWriter {
325 fn default() -> Self {
326 Self::new()
327 }
328}
329
330impl fmt::Display for Node {
331 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332 let mut writer = CommonMarkWriter::new();
333 match writer.write(self) {
334 Ok(_) => write!(f, "{}", writer.into_string()),
335 Err(e) => write!(f, "Error writing Node: {e}"),
336 }
337 }
338}