glib_macros/
object_impl_attributes.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3pub mod interface;
4pub mod subclass;
5
6use proc_macro2::Span;
7
8use crate::utils::{parse_optional_nested_meta_items, NestedMetaItem};
9
10/// The parsing of `#[object_subclass]` and `#[object_interface]` is subtly different.
11enum 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.
19pub 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
29impl 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]`
97struct MetaDynamic {
98    plugin_type: Option<syn::Path>,
99    lazy_registration: bool,
100}
101
102impl 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}