cmark_writer_macros/
lib.rs

1extern crate proc_macro;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{parse_macro_input, DeriveInput};
5
6// Note: The legacy `custom_node` attribute macro has been removed.
7
8/// Derive macro for automatically implementing MultiFormat trait for types that only support CommonMark
9/// This macro is intended for custom nodes that only implement Format<CommonMarkWriter>
10/// and want default HTML fallback behavior.
11///
12/// # Example
13///
14/// ```rust
15/// use cmark_writer_macros::CommonMarkOnly;
16///
17/// #[derive(CommonMarkOnly)]
18/// struct SimpleNote {
19///     content: String,
20/// }
21/// ```
22#[proc_macro_derive(CommonMarkOnly)]
23pub fn derive_commonmark_only(input: TokenStream) -> TokenStream {
24    let input = parse_macro_input!(input as DeriveInput);
25    let name = &input.ident;
26
27    let expanded = quote! {
28        impl ::cmark_writer::MultiFormat for #name {
29            fn supports_html(&self) -> bool {
30                false
31            }
32
33            fn html_format(&self, writer: &mut ::cmark_writer::HtmlWriter) -> ::cmark_writer::error::WriteResult<()> {
34                writer
35                    .raw_html(&format!(
36                        "<!-- HTML rendering not implemented for {} -->",
37                        std::any::type_name::<Self>()
38                    ))
39                    .map_err(Into::into)
40            }
41        }
42    };
43
44    TokenStream::from(expanded)
45}
46
47/// Custom error attribute macro, replaces the struct form errors in the original define_custom_errors! macro
48///
49/// # Example
50///
51/// ```rust
52/// use cmark_writer_macros::structure_error;
53///
54/// #[structure_error(format = "Table column mismatch: {}")]
55/// struct TableColumnMismatchError(pub &'static str);
56/// ```
57#[proc_macro_attribute]
58pub fn structure_error(attr: TokenStream, item: TokenStream) -> TokenStream {
59    let attr_str = attr.to_string();
60    let input = parse_macro_input!(item as DeriveInput);
61    let name = &input.ident;
62
63    // Parse format attribute
64    let format = if attr_str.starts_with("format") {
65        let format_str = attr_str
66            .replace("format", "")
67            .replace("=", "")
68            .trim()
69            .trim_matches('"')
70            .to_string();
71        format_str
72    } else {
73        // Default error message if format not specified
74        "{}".to_string()
75    };
76
77    let expanded = quote! {
78        #input
79
80        impl #name {
81            pub fn new(message: &'static str) -> Self {
82                Self(message)
83            }
84
85            pub fn into_error(self) -> ::cmark_writer::error::WriteError {
86                let mut error_factory = ::cmark_writer::error::StructureError::new(#format);
87
88                let arg = self.0.to_string();
89                error_factory = error_factory.arg(arg);
90
91                <::cmark_writer::error::StructureError as ::cmark_writer::error::CustomErrorFactory>::create_error(&error_factory)
92            }
93        }
94
95        impl From<#name> for ::cmark_writer::error::WriteError {
96            fn from(factory: #name) -> Self {
97                factory.into_error()
98            }
99        }
100
101        impl ::cmark_writer::error::CustomErrorFactory for #name {
102            fn create_error(&self) -> ::cmark_writer::error::WriteError {
103                let mut error_factory = ::cmark_writer::error::StructureError::new(#format);
104
105                let arg = self.0.to_string();
106                error_factory = error_factory.arg(arg);
107
108                <::cmark_writer::error::StructureError as ::cmark_writer::error::CustomErrorFactory>::create_error(&error_factory)
109            }
110        }
111    };
112
113    TokenStream::from(expanded)
114}
115
116/// Custom coded error attribute macro, replaces the coded form errors in the original define_custom_errors! macro
117///
118/// # Example
119///
120/// ```rust
121/// use cmark_writer_macros::coded_error;
122///
123/// #[coded_error]
124/// struct MarkdownSyntaxError(pub &'static str, pub &'static str);
125/// ```
126#[proc_macro_attribute]
127pub fn coded_error(_attr: TokenStream, item: TokenStream) -> TokenStream {
128    let input = parse_macro_input!(item as DeriveInput);
129    let name = &input.ident;
130
131    let expanded = quote! {
132        #input
133
134        impl #name {
135            pub fn new(message: &str, code: &str) -> Self {
136                Self(message.to_string(), code.to_string())
137            }
138
139            pub fn into_error(self) -> ::cmark_writer::error::WriteError {
140                let coded_error = ::cmark_writer::error::CodedError::new(self.0, self.1);
141                <::cmark_writer::error::CodedError as ::cmark_writer::error::CustomErrorFactory>::create_error(&coded_error)
142            }
143        }
144
145        impl From<#name> for ::cmark_writer::error::WriteError {
146            fn from(factory: #name) -> Self {
147                factory.into_error()
148            }
149        }
150
151        impl ::cmark_writer::error::CustomErrorFactory for #name {
152            fn create_error(&self) -> ::cmark_writer::error::WriteError {
153                let coded_error = ::cmark_writer::error::CodedError::new(self.0.clone(), self.1.clone());
154                <::cmark_writer::error::CodedError as ::cmark_writer::error::CustomErrorFactory>::create_error(&coded_error)
155            }
156        }
157    };
158
159    TokenStream::from(expanded)
160}