cmark_writer_macros/
lib.rs

1extern crate proc_macro;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{parse_macro_input, DeriveInput};
5
6/// Custom node attribute macro, replaces the original derive_custom_node! macro
7///
8/// This macro automatically implements the CustomNode trait, users only need to implement
9/// write_custom and is_block_custom methods
10///
11/// # Example
12///
13/// ```rust
14/// use cmark_writer_macros::custom_node;
15///
16/// #[derive(Debug, Clone, PartialEq)]
17/// #[custom_node]
18/// struct HighlightNode {
19///     content: String,
20///     color: String,
21/// }
22///
23/// impl HighlightNode {
24///     fn write_custom(&self, writer: &mut dyn CustomNodeWriter) -> WriteResult<()> {
25///         writer.write_str("<span style=\"background-color: ")?;
26///         writer.write_str(&self.color)?;
27///         writer.write_str("\">")?;
28///         writer.write_str(&self.content)?;
29///         writer.write_str("</span>")?;
30///         Ok(())
31///     }
32///     
33///     fn is_block_custom(&self) -> bool {
34///         false // This is an inline element
35///     }
36/// }
37/// ```
38#[proc_macro_attribute]
39pub fn custom_node(_attr: TokenStream, item: TokenStream) -> TokenStream {
40    let input = parse_macro_input!(item as DeriveInput);
41    let name = &input.ident;
42
43    let expanded = quote! {
44        #input
45
46        impl ::cmark_writer::ast::CustomNode for #name {
47            fn write(
48                &self,
49                writer: &mut dyn ::cmark_writer::ast::CustomNodeWriter,
50            ) -> ::cmark_writer::error::WriteResult<()> {
51                self.write_custom(writer)
52            }
53
54            fn clone_box(&self) -> Box<dyn ::cmark_writer::ast::CustomNode> {
55                Box::new(self.clone())
56            }
57
58            fn eq_box(&self, other: &dyn ::cmark_writer::ast::CustomNode) -> bool {
59                if let Some(other) = other.as_any().downcast_ref::<Self>() {
60                    self == other
61                } else {
62                    false
63                }
64            }
65
66            fn is_block(&self) -> bool {
67                self.is_block_custom()
68            }
69
70            fn as_any(&self) -> &dyn std::any::Any {
71                self
72            }
73        }
74    };
75
76    TokenStream::from(expanded)
77}
78
79/// Custom error attribute macro, replaces the struct form errors in the original define_custom_errors! macro
80///
81/// # Example
82///
83/// ```rust
84/// use cmark_writer_macros::custom_error;
85///
86/// #[custom_error(format = "Table column mismatch: {}")]
87/// struct TableColumnMismatchError(pub &'static str);
88/// ```
89#[proc_macro_attribute]
90pub fn custom_error(attr: TokenStream, item: TokenStream) -> TokenStream {
91    let attr_str = attr.to_string();
92    let input = parse_macro_input!(item as DeriveInput);
93    let name = &input.ident;
94
95    // Parse format attribute
96    let format = if attr_str.starts_with("format") {
97        let format_str = attr_str
98            .replace("format", "")
99            .replace("=", "")
100            .trim()
101            .trim_matches('"')
102            .to_string();
103        format_str
104    } else {
105        // Default error message if format not specified
106        "{}".to_string()
107    };
108
109    let expanded = quote! {
110        #input
111
112        impl #name {
113            pub fn new(message: &'static str) -> Self {
114                Self(message)
115            }
116
117            pub fn into_error(self) -> ::cmark_writer::error::WriteError {
118                let mut error_factory = ::cmark_writer::error::StructureError::new(#format);
119
120                let arg = self.0.to_string();
121                error_factory = error_factory.arg(arg);
122
123                <::cmark_writer::error::StructureError as ::cmark_writer::error::CustomErrorFactory>::create_error(&error_factory)
124            }
125        }
126
127        impl From<#name> for ::cmark_writer::error::WriteError {
128            fn from(factory: #name) -> Self {
129                factory.into_error()
130            }
131        }
132
133        impl ::cmark_writer::error::CustomErrorFactory for #name {
134            fn create_error(&self) -> ::cmark_writer::error::WriteError {
135                let mut error_factory = ::cmark_writer::error::StructureError::new(#format);
136
137                let arg = self.0.to_string();
138                error_factory = error_factory.arg(arg);
139
140                <::cmark_writer::error::StructureError as ::cmark_writer::error::CustomErrorFactory>::create_error(&error_factory)
141            }
142        }
143    };
144
145    TokenStream::from(expanded)
146}
147
148/// Custom coded error attribute macro, replaces the coded form errors in the original define_custom_errors! macro
149///
150/// # Example
151///
152/// ```rust
153/// use cmark_writer_macros::coded_error;
154///
155/// #[coded_error]
156/// struct MarkdownSyntaxError(pub &'static str, pub &'static str);
157/// ```
158#[proc_macro_attribute]
159pub fn coded_error(_attr: TokenStream, item: TokenStream) -> TokenStream {
160    let input = parse_macro_input!(item as DeriveInput);
161    let name = &input.ident;
162
163    let expanded = quote! {
164        #input
165
166        impl #name {
167            pub fn new(message: &str, code: &str) -> Self {
168                Self(message.to_string(), code.to_string())
169            }
170
171            pub fn into_error(self) -> ::cmark_writer::error::WriteError {
172                let coded_error = ::cmark_writer::error::CodedError::new(self.0, self.1);
173                <::cmark_writer::error::CodedError as ::cmark_writer::error::CustomErrorFactory>::create_error(&coded_error)
174            }
175        }
176
177        impl From<#name> for ::cmark_writer::error::WriteError {
178            fn from(factory: #name) -> Self {
179                factory.into_error()
180            }
181        }
182
183        impl ::cmark_writer::error::CustomErrorFactory for #name {
184            fn create_error(&self) -> ::cmark_writer::error::WriteError {
185                let coded_error = ::cmark_writer::error::CodedError::new(self.0.clone(), self.1.clone());
186                <::cmark_writer::error::CodedError as ::cmark_writer::error::CustomErrorFactory>::create_error(&coded_error)
187            }
188        }
189    };
190
191    TokenStream::from(expanded)
192}