cmark_writer/ast/custom.rs
1//! Custom node definitions for the CommonMark AST.
2
3use crate::error::{WriteError, WriteResult};
4use crate::writer::{BlockWriterProxy, HtmlWriteResult, HtmlWriter, InlineWriterProxy};
5use std::any::Any;
6
7/// Trait for implementing custom node behavior for the CommonMark AST.
8///
9/// This trait defines methods that all custom node types must implement.
10/// Users can implement dedicated block or inline rendering methods for CommonMark output and
11/// optionally override the `html_write` method for HTML output.
12///
13/// The recommended way to implement this trait is through the `custom_node` macro,
14/// which provides a default implementation of most methods and requires users to
15/// implement only the node-specific logic.
16///
17/// # Example
18///
19/// ```rust
20/// use ecow::EcoString;
21/// use cmark_writer_macros::custom_node;
22/// use cmark_writer::error::WriteResult;
23/// use cmark_writer::writer::{HtmlWriteResult, HtmlWriter, InlineWriterProxy};
24///
25/// // Define a custom node with support for both CommonMark and HTML output
26/// #[derive(Debug, Clone, PartialEq)]
27/// #[custom_node(block=false, html_impl=true)]
28/// struct HighlightNode {
29/// content: EcoString,
30/// color: EcoString,
31/// }
32///
33/// impl HighlightNode {
34/// // Required for CommonMark output
35/// fn write_custom(&self, writer: &mut InlineWriterProxy) -> WriteResult<()> {
36/// writer.write_str("<span style=\"background-color: ")?;
37/// writer.write_str(&self.color)?;
38/// writer.write_str("\">")?;
39/// writer.write_str(&self.content)?;
40/// writer.write_str("</span>")?;
41/// Ok(())
42/// }
43///
44/// // Optional HTML-specific implementation
45/// fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
46/// writer.start_tag("span")?;
47/// writer.attribute("style", &format!("background-color: {}", self.color))?;
48/// writer.finish_tag()?;
49/// writer.text(&self.content)?;
50/// writer.end_tag("span")?;
51/// Ok(())
52/// }
53/// }
54/// ```
55pub trait CustomNode: std::fmt::Debug + Send + Sync {
56 /// Write the custom node as a block element using the restricted block writer proxy.
57 ///
58 /// Block custom nodes should implement this method to emit valid block-level content.
59 fn write_block(&self, writer: &mut BlockWriterProxy) -> WriteResult<()> {
60 let _ = writer;
61 Err(WriteError::UnsupportedNodeType)
62 }
63
64 /// Write the custom node as an inline element using the restricted inline writer proxy.
65 ///
66 /// Inline custom nodes should implement this method to emit valid inline content.
67 fn write_inline(&self, writer: &mut InlineWriterProxy) -> WriteResult<()> {
68 let _ = writer;
69 Err(WriteError::UnsupportedNodeType)
70 }
71
72 /// Writes the HTML representation of the custom node to the provided HTML writer.
73 ///
74 /// By default, this writes an HTML comment indicating that HTML rendering is not implemented
75 /// for this custom node type. When using the `custom_node` macro with `html_impl=true`,
76 /// this method delegates to the user-defined `write_html_custom` method.
77 ///
78 /// Users should either:
79 /// 1. Override this method directly, or
80 /// 2. Use the `custom_node` macro with `html_impl=true` and implement the `write_html_custom` method.
81 fn html_write(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
82 writer.write_trusted_html(&format!(
83 "<!-- HTML rendering not implemented for Custom Node: {} -->",
84 self.type_name()
85 ))?;
86 Ok(())
87 }
88
89 /// Clone the custom node
90 fn clone_box(&self) -> Box<dyn CustomNode>;
91
92 /// Check if two custom nodes are equal
93 fn eq_box(&self, other: &dyn CustomNode) -> bool;
94
95 /// Whether the custom node is a block element
96 fn is_block(&self) -> bool;
97
98 /// Convert to Any for type casting
99 fn as_any(&self) -> &dyn Any;
100
101 /// Convert to mutable Any for type casting
102 fn as_any_mut(&mut self) -> &mut dyn Any;
103
104 /// Get the type name of the custom node for pattern matching
105 fn type_name(&self) -> &'static str {
106 std::any::type_name::<Self>()
107 }
108}
109
110// NOTE: CustomNodeWriter trait is deprecated and will be removed in a future version.
111// Custom nodes should now directly use the provided writer proxies instead.
112/*
113/// Trait for custom node writer implementation
114pub trait CustomNodeWriter {
115 /// Write a string to the output
116 fn write_str(&mut self, s: &str) -> std::fmt::Result;
117
118 /// Write a character to the output
119 fn write_char(&mut self, c: char) -> std::fmt::Result;
120}
121*/
122
123// Implement Clone for Box<dyn CustomNode>
124impl Clone for Box<dyn CustomNode> {
125 fn clone(&self) -> Self {
126 self.clone_box()
127 }
128}
129
130// Implement PartialEq for Box<dyn CustomNode>
131impl PartialEq for Box<dyn CustomNode> {
132 fn eq(&self, other: &Self) -> bool {
133 self.eq_box(&**other)
134 }
135}