cmark_writer_macros/
lib.rs1extern crate proc_macro;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{
5 parse::Parse, parse::ParseStream, parse_macro_input, DeriveInput, Ident, LitBool, Token,
6};
7
8struct CustomNodeArgs {
10 is_block: Option<bool>,
11 html_impl: Option<bool>,
12}
13
14impl Parse for CustomNodeArgs {
15 fn parse(input: ParseStream) -> syn::Result<Self> {
16 let mut is_block = None;
17 let mut html_impl = None;
18
19 if input.is_empty() {
20 return Ok(CustomNodeArgs {
21 is_block,
22 html_impl,
23 });
24 }
25
26 loop {
27 if input.is_empty() {
28 break;
29 }
30
31 let ident: Ident = input.parse()?;
32
33 if ident == "block" {
34 let _: Token![=] = input.parse()?;
35 let value: LitBool = input.parse()?;
36 is_block = Some(value.value);
37 } else if ident == "html_impl" {
38 let _: Token![=] = input.parse()?;
39 let value: LitBool = input.parse()?;
40 html_impl = Some(value.value);
41 } else {
42 return Err(syn::Error::new_spanned(
43 ident,
44 "Unknown attribute parameter",
45 ));
46 }
47
48 if input.peek(Token![,]) {
50 let _: Token![,] = input.parse()?;
51 }
52 }
53
54 Ok(CustomNodeArgs {
55 is_block,
56 html_impl,
57 })
58 }
59}
60
61#[proc_macro_attribute]
120pub fn custom_node(attr: TokenStream, item: TokenStream) -> TokenStream {
121 let args = syn::parse_macro_input!(attr as CustomNodeArgs);
122 let input = parse_macro_input!(item as DeriveInput);
123 let name = &input.ident;
124
125 let is_block_impl = if let Some(is_block) = args.is_block {
127 quote! {
128 fn is_block(&self) -> bool {
129 #is_block
130 }
131 }
132 } else {
133 quote! {
134 fn is_block(&self) -> bool {
135 self.is_block_custom()
136 }
137 }
138 };
139
140 let html_write_impl = if args.html_impl.unwrap_or(false) {
142 quote! {
144 fn html_write(
145 &self,
146 writer: &mut ::cmark_writer::writer::HtmlWriter,
147 ) -> ::cmark_writer::writer::HtmlWriteResult<()> {
148 self.write_html_custom(writer)
149 }
150 }
151 } else {
152 quote! {
154 fn html_write(
155 &self,
156 writer: &mut ::cmark_writer::writer::HtmlWriter,
157 ) -> ::cmark_writer::writer::HtmlWriteResult<()> {
158 writer.raw_html(&format!(
159 "<!-- HTML rendering not implemented for Custom Node: {} -->",
160 self.type_name()
161 ))?;
162 Ok(())
163 }
164 }
165 };
166
167 let expanded = quote! {
168 #input
169
170 impl ::cmark_writer::ast::CustomNode for #name {
171 fn write(
172 &self,
173 writer: &mut ::cmark_writer::writer::CommonMarkWriter,
174 ) -> ::cmark_writer::error::WriteResult<()> {
175 self.write_custom(writer)
176 }
177
178 #html_write_impl
179
180 fn clone_box(&self) -> Box<dyn ::cmark_writer::ast::CustomNode> {
181 Box::new(self.clone())
182 }
183
184 fn eq_box(&self, other: &dyn ::cmark_writer::ast::CustomNode) -> bool {
185 if let Some(other) = other.as_any().downcast_ref::<Self>() {
186 self == other
187 } else {
188 false
189 }
190 }
191
192 #is_block_impl
193
194 fn as_any(&self) -> &dyn std::any::Any {
195 self
196 }
197
198 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
199 self
200 }
201 }
202
203 impl #name {
204 pub fn matches(node: &dyn ::cmark_writer::ast::CustomNode) -> bool {
205 node.type_name() == std::any::type_name::<#name>() ||
206 node.as_any().downcast_ref::<#name>().is_some()
207 }
208
209 pub fn extract(node: Box<dyn ::cmark_writer::ast::CustomNode>) -> Option<#name> {
210 node.as_any().downcast_ref::<#name>().map(|n| n.clone())
211 }
212 }
213 };
214
215 TokenStream::from(expanded)
216}
217
218#[proc_macro_attribute]
229pub fn structure_error(attr: TokenStream, item: TokenStream) -> TokenStream {
230 let attr_str = attr.to_string();
231 let input = parse_macro_input!(item as DeriveInput);
232 let name = &input.ident;
233
234 let format = if attr_str.starts_with("format") {
236 let format_str = attr_str
237 .replace("format", "")
238 .replace("=", "")
239 .trim()
240 .trim_matches('"')
241 .to_string();
242 format_str
243 } else {
244 "{}".to_string()
246 };
247
248 let expanded = quote! {
249 #input
250
251 impl #name {
252 pub fn new(message: &'static str) -> Self {
253 Self(message)
254 }
255
256 pub fn into_error(self) -> ::cmark_writer::error::WriteError {
257 let mut error_factory = ::cmark_writer::error::StructureError::new(#format);
258
259 let arg = self.0.to_string();
260 error_factory = error_factory.arg(arg);
261
262 <::cmark_writer::error::StructureError as ::cmark_writer::error::CustomErrorFactory>::create_error(&error_factory)
263 }
264 }
265
266 impl From<#name> for ::cmark_writer::error::WriteError {
267 fn from(factory: #name) -> Self {
268 factory.into_error()
269 }
270 }
271
272 impl ::cmark_writer::error::CustomErrorFactory for #name {
273 fn create_error(&self) -> ::cmark_writer::error::WriteError {
274 let mut error_factory = ::cmark_writer::error::StructureError::new(#format);
275
276 let arg = self.0.to_string();
277 error_factory = error_factory.arg(arg);
278
279 <::cmark_writer::error::StructureError as ::cmark_writer::error::CustomErrorFactory>::create_error(&error_factory)
280 }
281 }
282 };
283
284 TokenStream::from(expanded)
285}
286
287#[proc_macro_attribute]
298pub fn coded_error(_attr: TokenStream, item: TokenStream) -> TokenStream {
299 let input = parse_macro_input!(item as DeriveInput);
300 let name = &input.ident;
301
302 let expanded = quote! {
303 #input
304
305 impl #name {
306 pub fn new(message: &str, code: &str) -> Self {
307 Self(message.to_string(), code.to_string())
308 }
309
310 pub fn into_error(self) -> ::cmark_writer::error::WriteError {
311 let coded_error = ::cmark_writer::error::CodedError::new(self.0, self.1);
312 <::cmark_writer::error::CodedError as ::cmark_writer::error::CustomErrorFactory>::create_error(&coded_error)
313 }
314 }
315
316 impl From<#name> for ::cmark_writer::error::WriteError {
317 fn from(factory: #name) -> Self {
318 factory.into_error()
319 }
320 }
321
322 impl ::cmark_writer::error::CustomErrorFactory for #name {
323 fn create_error(&self) -> ::cmark_writer::error::WriteError {
324 let coded_error = ::cmark_writer::error::CodedError::new(self.0.clone(), self.1.clone());
325 <::cmark_writer::error::CodedError as ::cmark_writer::error::CustomErrorFactory>::create_error(&coded_error)
326 }
327 }
328 };
329
330 TokenStream::from(expanded)
331}