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 | |