| 1 | // Take a look at the license at the top of the repository in the LICENSE file. |
| 2 | |
| 3 | pub mod interface; |
| 4 | pub mod subclass; |
| 5 | |
| 6 | use proc_macro2::Span; |
| 7 | |
| 8 | use crate::utils::{parse_optional_nested_meta_items, NestedMetaItem}; |
| 9 | |
| 10 | /// The parsing of `#[object_subclass]` and `#[object_interface]` is subtly different. |
| 11 | enum AttrKind { |
| 12 | Interface, |
| 13 | Subclass, |
| 14 | } |
| 15 | |
| 16 | /// The parsed input for the object impl attributes.. |
| 17 | /// |
| 18 | /// This is used for both the `#[object_subclass]` and `#[object_interface]` attributes. |
| 19 | pub struct Input { |
| 20 | attrs: Vec<syn::Attribute>, |
| 21 | generics: syn::Generics, |
| 22 | trait_path: syn::Path, |
| 23 | self_ty: syn::Ident, |
| 24 | unsafety: Option<syn::token::Unsafe>, |
| 25 | items: Vec<syn::ImplItem>, |
| 26 | meta_dynamic: Option<MetaDynamic>, |
| 27 | } |
| 28 | |
| 29 | impl Input { |
| 30 | /// Parse an `#[object_interface]` attribute. |
| 31 | pub fn parse_interface(input: syn::parse::ParseStream) -> syn::Result<Self> { |
| 32 | Self::parse(AttrKind::Interface, input) |
| 33 | } |
| 34 | |
| 35 | /// Parse an `#[object_subclass]` attribute. |
| 36 | pub fn parse_subclass(input: syn::parse::ParseStream) -> syn::Result<Self> { |
| 37 | Self::parse(AttrKind::Subclass, input) |
| 38 | } |
| 39 | |
| 40 | /// Parse an `#[object_subclass]` or `#[object_interface]` depending on the attribute kind. |
| 41 | fn parse(kind: AttrKind, input: syn::parse::ParseStream) -> syn::Result<Self> { |
| 42 | let wrong_place_msg = match kind { |
| 43 | AttrKind::Interface => { |
| 44 | "This macro should be used on `impl` block for `glib::ObjectInterface` trait" |
| 45 | } |
| 46 | AttrKind::Subclass => { |
| 47 | "This macro should be used on `impl` block for `glib::ObjectSubclass` trait" |
| 48 | } |
| 49 | }; |
| 50 | |
| 51 | let syn::ItemImpl { |
| 52 | mut attrs, |
| 53 | generics, |
| 54 | trait_, |
| 55 | self_ty, |
| 56 | unsafety, |
| 57 | items, |
| 58 | .. |
| 59 | } = input |
| 60 | .parse() |
| 61 | .map_err(|_| syn::Error::new(Span::call_site(), wrong_place_msg))?; |
| 62 | |
| 63 | // The type must be a path |
| 64 | let self_ty = match *self_ty { |
| 65 | syn::Type::Path(syn::TypePath { path, .. }) => path.require_ident().cloned(), |
| 66 | _ => Err(syn::Error::new( |
| 67 | syn::spanned::Spanned::span(&self_ty), |
| 68 | "expected this path to be an identifier" , |
| 69 | )), |
| 70 | }?; |
| 71 | |
| 72 | let meta_dynamic = MetaDynamic::parse_and_remove(kind, &mut attrs)?; |
| 73 | |
| 74 | let trait_path = trait_ |
| 75 | .as_ref() |
| 76 | .ok_or_else(|| syn::Error::new(Span::call_site(), wrong_place_msg))? |
| 77 | .1 |
| 78 | .clone(); |
| 79 | |
| 80 | Ok(Self { |
| 81 | attrs, |
| 82 | generics, |
| 83 | trait_path, |
| 84 | self_ty, |
| 85 | unsafety, |
| 86 | items, |
| 87 | meta_dynamic, |
| 88 | }) |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | /// A meta attribute to indicate that the class / interface is dynamic. |
| 93 | /// |
| 94 | /// Depending on the object kind this can be either |
| 95 | /// - `#[object_subclass_dynamic]` |
| 96 | /// - `#[object_interface_dynamic]` |
| 97 | struct MetaDynamic { |
| 98 | plugin_type: Option<syn::Path>, |
| 99 | lazy_registration: bool, |
| 100 | } |
| 101 | |
| 102 | impl MetaDynamic { |
| 103 | /// Parse `#[object_subclass_dynamic]` / `#[object_interface_dynamic]` |
| 104 | fn parse_and_remove( |
| 105 | kind: AttrKind, |
| 106 | attrs: &mut Vec<syn::Attribute>, |
| 107 | ) -> syn::Result<Option<Self>> { |
| 108 | let attr_name = match kind { |
| 109 | AttrKind::Interface => "object_interface_dynamic" , |
| 110 | AttrKind::Subclass => "object_subclass_dynamic" , |
| 111 | }; |
| 112 | |
| 113 | let mut plugin_type = NestedMetaItem::<syn::Path>::new("plugin_type" ).value_required(); |
| 114 | let mut lazy_registration = |
| 115 | NestedMetaItem::<syn::LitBool>::new("lazy_registration" ).value_required(); |
| 116 | |
| 117 | let found = parse_optional_nested_meta_items( |
| 118 | &*attrs, |
| 119 | attr_name, |
| 120 | &mut [&mut plugin_type, &mut lazy_registration], |
| 121 | )? |
| 122 | .is_some(); |
| 123 | |
| 124 | if found { |
| 125 | // remove attribute from the attribute list because it is not a real proc_macro_attribute |
| 126 | attrs.retain(|attr| !attr.path().is_ident(attr_name)); |
| 127 | |
| 128 | Ok(Some(Self { |
| 129 | plugin_type: plugin_type.value, |
| 130 | lazy_registration: lazy_registration.value.map(|b| b.value).unwrap_or_default(), |
| 131 | })) |
| 132 | } else { |
| 133 | Ok(None) |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |