summaryrefslogtreecommitdiff
path: root/macros/src/capability.rs
diff options
context:
space:
mode:
Diffstat (limited to 'macros/src/capability.rs')
-rw-r--r--macros/src/capability.rs44
1 files changed, 41 insertions, 3 deletions
diff --git a/macros/src/capability.rs b/macros/src/capability.rs
index 7dd4c42a..aa98f584 100644
--- a/macros/src/capability.rs
+++ b/macros/src/capability.rs
@@ -1,10 +1,48 @@
use super::*;
+use syn::parse::Parser;
+use syn::punctuated::Punctuated;
+use syn::Token;
+
/// Expand the `#[capability]` macro.
-pub fn expand(body: syn::ItemTrait) -> Result<TokenStream> {
- let ident = &body.ident;
+pub fn capability(item: syn::ItemTrait) -> Result<TokenStream> {
+ let ident = &item.ident;
Ok(quote! {
- #body
+ #item
impl ::typst::model::Capability for dyn #ident {}
})
}
+
+/// Expand the `#[capable(..)]` macro.
+pub fn capable(attr: TokenStream, item: syn::Item) -> Result<TokenStream> {
+ 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 and enums are supported"),
+ };
+
+ let (params, args, clause) = generics.split_for_impl();
+ let checks = Punctuated::<Ident, Token![,]>::parse_terminated
+ .parse2(attr)?
+ .into_iter()
+ .map(|capability| {
+ quote! {
+ if id == ::std::any::TypeId::of::<dyn #capability>() {
+ return Some(unsafe {
+ ::typst::util::fat::vtable(self as &dyn #capability)
+ });
+ }
+ }
+ });
+
+ Ok(quote! {
+ #item
+
+ unsafe impl #params ::typst::model::Capable for #ident #args #clause {
+ fn vtable(&self, id: ::std::any::TypeId) -> ::std::option::Option<*const ()> {
+ #(#checks)*
+ None
+ }
+ }
+ })
+}