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]
119pub fn custom_node(attr: TokenStream, item: TokenStream) -> TokenStream {
120 let args = syn::parse_macro_input!(attr as CustomNodeArgs);
121 let input = parse_macro_input!(item as DeriveInput);
122 let name = &input.ident;
123
124 let is_block_impl = if let Some(is_block) = args.is_block {
126 quote! {
127 fn is_block(&self) -> bool {
128 #is_block
129 }
130 }
131 } else {
132 quote! {
133 fn is_block(&self) -> bool {
134 self.is_block_custom()
135 }
136 }
137 };
138
139 let html_write_impl = if args.html_impl.unwrap_or(false) {
141 quote! {
143 fn html_write(
144 &self,
145 writer: &mut ::cmark_writer::writer::HtmlWriter,
146 ) -> ::cmark_writer::writer::HtmlWriteResult<()> {
147 self.write_html_custom(writer)
148 }
149 }
150 } else {
151 quote! {
153 fn html_write(
154 &self,
155 writer: &mut ::cmark_writer::writer::HtmlWriter,
156 ) -> ::cmark_writer::writer::HtmlWriteResult<()> {
157 writer.raw_html(&format!(
158 "<!-- HTML rendering not implemented for Custom Node: {} -->",
159 self.type_name()
160 ))?;
161 Ok(())
162 }
163 }
164 };
165
166 let expanded = quote! {
167 #input
168
169 impl ::cmark_writer::ast::CustomNode for #name {
170 fn write(
171 &self,
172 writer: &mut ::cmark_writer::writer::CommonMarkWriter,
173 ) -> ::cmark_writer::error::WriteResult<()> {
174 self.write_custom(writer)
175 }
176
177 #html_write_impl
178
179 fn clone_box(&self) -> Box<dyn ::cmark_writer::ast::CustomNode> {
180 Box::new(self.clone())
181 }
182
183 fn eq_box(&self, other: &dyn ::cmark_writer::ast::CustomNode) -> bool {
184 if let Some(other) = other.as_any().downcast_ref::<Self>() {
185 self == other
186 } else {
187 false
188 }
189 }
190
191 #is_block_impl
192
193 fn as_any(&self) -> &dyn std::any::Any {
194 self
195 }
196
197 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
198 self
199 }
200 }
201
202 impl #name {
203 pub fn matches(node: &dyn ::cmark_writer::ast::CustomNode) -> bool {
204 node.type_name() == std::any::type_name::<#name>() ||
205 node.as_any().downcast_ref::<#name>().is_some()
206 }
207
208 pub fn extract(node: Box<dyn ::cmark_writer::ast::CustomNode>) -> Option<#name> {
209 node.as_any().downcast_ref::<#name>().map(|n| n.clone())
210 }
211 }
212 };
213
214 TokenStream::from(expanded)
215}
216
217#[proc_macro_attribute]
228pub fn structure_error(attr: TokenStream, item: TokenStream) -> TokenStream {
229 let attr_str = attr.to_string();
230 let input = parse_macro_input!(item as DeriveInput);
231 let name = &input.ident;
232
233 let format = if attr_str.starts_with("format") {
235 let format_str = attr_str
236 .replace("format", "")
237 .replace("=", "")
238 .trim()
239 .trim_matches('"')
240 .to_string();
241 format_str
242 } else {
243 "{}".to_string()
245 };
246
247 let expanded = quote! {
248 #input
249
250 impl #name {
251 pub fn new(message: &'static str) -> Self {
252 Self(message)
253 }
254
255 pub fn into_error(self) -> ::cmark_writer::error::WriteError {
256 let mut error_factory = ::cmark_writer::error::StructureError::new(#format);
257
258 let arg = self.0.to_string();
259 error_factory = error_factory.arg(arg);
260
261 <::cmark_writer::error::StructureError as ::cmark_writer::error::CustomErrorFactory>::create_error(&error_factory)
262 }
263 }
264
265 impl From<#name> for ::cmark_writer::error::WriteError {
266 fn from(factory: #name) -> Self {
267 factory.into_error()
268 }
269 }
270
271 impl ::cmark_writer::error::CustomErrorFactory for #name {
272 fn create_error(&self) -> ::cmark_writer::error::WriteError {
273 let mut error_factory = ::cmark_writer::error::StructureError::new(#format);
274
275 let arg = self.0.to_string();
276 error_factory = error_factory.arg(arg);
277
278 <::cmark_writer::error::StructureError as ::cmark_writer::error::CustomErrorFactory>::create_error(&error_factory)
279 }
280 }
281 };
282
283 TokenStream::from(expanded)
284}
285
286#[proc_macro_attribute]
297pub fn coded_error(_attr: TokenStream, item: TokenStream) -> TokenStream {
298 let input = parse_macro_input!(item as DeriveInput);
299 let name = &input.ident;
300
301 let expanded = quote! {
302 #input
303
304 impl #name {
305 pub fn new(message: &str, code: &str) -> Self {
306 Self(message.to_string(), code.to_string())
307 }
308
309 pub fn into_error(self) -> ::cmark_writer::error::WriteError {
310 let coded_error = ::cmark_writer::error::CodedError::new(self.0, self.1);
311 <::cmark_writer::error::CodedError as ::cmark_writer::error::CustomErrorFactory>::create_error(&coded_error)
312 }
313 }
314
315 impl From<#name> for ::cmark_writer::error::WriteError {
316 fn from(factory: #name) -> Self {
317 factory.into_error()
318 }
319 }
320
321 impl ::cmark_writer::error::CustomErrorFactory for #name {
322 fn create_error(&self) -> ::cmark_writer::error::WriteError {
323 let coded_error = ::cmark_writer::error::CodedError::new(self.0.clone(), self.1.clone());
324 <::cmark_writer::error::CodedError as ::cmark_writer::error::CustomErrorFactory>::create_error(&coded_error)
325 }
326 }
327 };
328
329 TokenStream::from(expanded)
330}