diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-12-14 10:09:44 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-12-14 10:09:44 +0100 |
| commit | 9ba4d2c134479aad876a0e2ac4cd1622a353109e (patch) | |
| tree | a94e0e6ae53a1ba440e869fca26cc2ea0b179057 /macros/src/func.rs | |
| parent | 4c73456fc1f5df8ebb3a89d9db657c3c54624d66 (diff) | |
New macro setup
Diffstat (limited to 'macros/src/func.rs')
| -rw-r--r-- | macros/src/func.rs | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/macros/src/func.rs b/macros/src/func.rs new file mode 100644 index 00000000..af558f96 --- /dev/null +++ b/macros/src/func.rs @@ -0,0 +1,82 @@ +use super::*; + +/// Expand the `#[func]` macro. +pub fn func(item: syn::Item) -> Result<TokenStream> { + let doc = documentation(&item)?; + + if let syn::Item::Fn(item) = &item { + let vis = &item.vis; + let ident = &item.sig.ident; + let s = ident.to_string(); + let mut chars = s.trim_end_matches("_").chars(); + let ty = quote::format_ident!( + "{}{}Func", + chars.next().unwrap().to_ascii_uppercase(), + chars.as_str() + ); + + let full = if item.sig.inputs.len() == 1 { + quote! { |_, args| #ident(args) } + } else { + quote! { #ident } + }; + + Ok(quote! { + #item + + #[doc(hidden)] + #vis enum #ty {} + + impl::typst::model::FuncType for #ty { + fn create_func(name: &'static str) -> ::typst::model::Func { + ::typst::model::Func::from_fn(name, #full, #doc) + } + } + }) + } else { + let (ident, generics) = match &item { + syn::Item::Struct(s) => (&s.ident, &s.generics), + syn::Item::Enum(s) => (&s.ident, &s.generics), + _ => bail!(item, "only structs, enums, and functions are supported"), + }; + + let (params, args, clause) = generics.split_for_impl(); + + Ok(quote! { + #item + + impl #params ::typst::model::FuncType for #ident #args #clause { + fn create_func(name: &'static str) -> ::typst::model::Func { + ::typst::model::Func::from_node::<Self>(name, #doc) + } + } + }) + } +} + +/// Extract the item's documentation. +fn documentation(item: &syn::Item) -> Result<String> { + let mut doc = String::new(); + + // Extract attributes. + let attrs = match item { + syn::Item::Struct(item) => &item.attrs, + syn::Item::Enum(item) => &item.attrs, + syn::Item::Fn(item) => &item.attrs, + _ => return Ok(doc), + }; + + // Parse doc comments. + for attr in attrs { + if let syn::Meta::NameValue(meta) = attr.parse_meta()? { + if meta.path.is_ident("doc") { + if let syn::Lit::Str(string) = &meta.lit { + doc.push_str(&string.value()); + doc.push('\n'); + } + } + } + } + + Ok(doc.trim().into()) +} |
