diff --git a/glib-macros/src/lib.rs b/glib-macros/src/lib.rs index 564ad408b7e1..5345a5f81dfc 100644 --- a/glib-macros/src/lib.rs +++ b/glib-macros/src/lib.rs @@ -8,8 +8,7 @@ mod downgrade_derive; mod enum_derive; mod error_domain_derive; mod flags_attribute; -mod object_interface_attribute; -mod object_subclass_attribute; +mod object_impl_attributes; mod properties; mod shared_boxed_derive; mod value_delegate_derive; @@ -906,16 +905,8 @@ pub fn shared_boxed_derive(input: TokenStream) -> TokenStream { /// [`TypePluginExt::unuse`]: ../glib/gobject/type_plugin/trait.TypePluginExt.html#method.unuse #[proc_macro_attribute] pub fn object_subclass(_attr: TokenStream, item: TokenStream) -> TokenStream { - syn::parse::(item) - .map_err(|_| { - syn::Error::new( - Span::call_site(), - object_subclass_attribute::WRONG_PLACE_MSG, - ) - }) - .and_then(|mut input| object_subclass_attribute::impl_object_subclass(&mut input)) - .unwrap_or_else(syn::Error::into_compile_error) - .into() + let input = parse_macro_input!(item with object_impl_attributes::Input::parse_subclass); + object_impl_attributes::subclass::impl_object_subclass(input).into() } /// Macro for boilerplate of [`ObjectInterface`] implementations. @@ -1024,16 +1015,8 @@ pub fn object_subclass(_attr: TokenStream, item: TokenStream) -> TokenStream { /// [`TypePluginExt::unuse`]: ../glib/gobject/type_plugin/trait.TypePluginExt.html#method.unuse/// #[proc_macro_attribute] pub fn object_interface(_attr: TokenStream, item: TokenStream) -> TokenStream { - syn::parse::(item) - .map_err(|_| { - syn::Error::new( - Span::call_site(), - object_interface_attribute::WRONG_PLACE_MSG, - ) - }) - .and_then(|mut input| object_interface_attribute::impl_object_interface(&mut input)) - .unwrap_or_else(syn::Error::into_compile_error) - .into() + let input = parse_macro_input!(item with object_impl_attributes::Input::parse_interface); + object_impl_attributes::interface::impl_object_interface(input).into() } /// Macro for deriving implementations of [`glib::clone::Downgrade`] and diff --git a/glib-macros/src/object_impl_attributes.rs b/glib-macros/src/object_impl_attributes.rs new file mode 100644 index 000000000000..30780e5398f5 --- /dev/null +++ b/glib-macros/src/object_impl_attributes.rs @@ -0,0 +1,136 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +pub mod interface; +pub mod subclass; + +use proc_macro2::Span; + +use crate::utils::{parse_optional_nested_meta_items, NestedMetaItem}; + +/// The parsing of `#[object_subclass]` and `#[object_interface]` is subtly different. +enum AttrKind { + Interface, + Subclass, +} + +/// The parsed input for the object impl attributes.. +/// +/// This is used for both the `#[object_subclass]` and `#[object_interface]` attributes. +pub struct Input { + attrs: Vec, + generics: syn::Generics, + trait_path: syn::Path, + self_ty: syn::Ident, + unsafety: Option, + items: Vec, + meta_dynamic: Option, +} + +impl Input { + /// Parse an `#[object_interface]` attribute. + pub fn parse_interface(input: syn::parse::ParseStream) -> syn::Result { + Self::parse(AttrKind::Interface, input) + } + + /// Parse an `#[object_subclass]` attribute. + pub fn parse_subclass(input: syn::parse::ParseStream) -> syn::Result { + Self::parse(AttrKind::Subclass, input) + } + + /// Parse an `#[object_subclass]` or `#[object_interface]` depending on the attribute kind. + fn parse(kind: AttrKind, input: syn::parse::ParseStream) -> syn::Result { + let wrong_place_msg = match kind { + AttrKind::Interface => { + "This macro should be used on `impl` block for `glib::ObjectInterface` trait" + } + AttrKind::Subclass => { + "This macro should be used on `impl` block for `glib::ObjectSubclass` trait" + } + }; + + let syn::ItemImpl { + mut attrs, + generics, + trait_, + self_ty, + unsafety, + items, + .. + } = input + .parse() + .map_err(|_| syn::Error::new(Span::call_site(), wrong_place_msg))?; + + // The type must be a path + let self_ty = match *self_ty { + syn::Type::Path(syn::TypePath { path, .. }) => path.require_ident().cloned(), + _ => Err(syn::Error::new( + syn::spanned::Spanned::span(&self_ty), + "expected this path to be an identifier", + )), + }?; + + let meta_dynamic = MetaDynamic::parse_and_remove(kind, &mut attrs)?; + + let trait_path = trait_ + .as_ref() + .ok_or_else(|| syn::Error::new(Span::call_site(), wrong_place_msg))? + .1 + .clone(); + + Ok(Self { + attrs, + generics, + trait_path, + self_ty, + unsafety, + items, + meta_dynamic, + }) + } +} + +/// A meta attribute to indicate that the class / interface is dynamic. +/// +/// Depending on the object kind this can be either +/// - `#[object_subclass_dynamic]` +/// - `#[object_interface_dynamic]` +struct MetaDynamic { + plugin_type: Option, + lazy_registration: bool, +} + +impl MetaDynamic { + /// Parse `#[object_subclass_dynamic]` / `#[object_interface_dynamic]` + fn parse_and_remove( + kind: AttrKind, + attrs: &mut Vec, + ) -> syn::Result> { + let attr_name = match kind { + AttrKind::Interface => "object_interface_dynamic", + AttrKind::Subclass => "object_subclass_dynamic", + }; + + let mut plugin_type = NestedMetaItem::::new("plugin_type").value_required(); + let mut lazy_registration = + NestedMetaItem::::new("lazy_registration").value_required(); + + let found = parse_optional_nested_meta_items( + &*attrs, + attr_name, + &mut [&mut plugin_type, &mut lazy_registration], + )? + .is_some(); + + if found { + // remove attribute from the attribute list because it is not a real proc_macro_attribute + attrs.retain(|attr| !attr.path().is_ident(attr_name)); + + Ok(Some(Self { + plugin_type: plugin_type.value, + lazy_registration: lazy_registration.value.map(|b| b.value).unwrap_or_default(), + })) + } else { + Ok(None) + } + } +} diff --git a/glib-macros/src/object_interface_attribute.rs b/glib-macros/src/object_impl_attributes/interface.rs similarity index 84% rename from glib-macros/src/object_interface_attribute.rs rename to glib-macros/src/object_impl_attributes/interface.rs index 488001d3645d..c9e79bb5cd98 100644 --- a/glib-macros/src/object_interface_attribute.rs +++ b/glib-macros/src/object_impl_attributes/interface.rs @@ -1,61 +1,34 @@ // Take a look at the license at the top of the repository in the LICENSE file. use heck::ToShoutySnakeCase; -use proc_macro2::{Span, TokenStream}; +use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; -use crate::utils::{parse_optional_nested_meta_items, NestedMetaItem}; - -pub const WRONG_PLACE_MSG: &str = - "This macro should be used on `impl` block for `glib::ObjectInterface` trait"; - -pub fn impl_object_interface(input: &mut syn::ItemImpl) -> syn::Result { +pub fn impl_object_interface(input: super::Input) -> TokenStream { let crate_ident = crate::utils::crate_ident_new(); - let syn::ItemImpl { + let super::Input { attrs, generics, - trait_, + trait_path, self_ty, unsafety, items, - .. + meta_dynamic, } = input; - let self_ty_as_ident = match &**self_ty { - syn::Type::Path(syn::TypePath { path, .. }) => path.require_ident(), - _ => Err(syn::Error::new_spanned( + let register_object_interface = if let Some(dynamic) = meta_dynamic { + let plugin_ty = dynamic + .plugin_type + .map(|p| p.into_token_stream()) + .unwrap_or(quote!(#crate_ident::TypeModule)); + register_object_interface_as_dynamic( + &crate_ident, &self_ty, - "expected this path to be an identifier", - )), - }?; - - let mut plugin_type = NestedMetaItem::::new("plugin_type").value_required(); - let mut lazy_registration = - NestedMetaItem::::new("lazy_registration").value_required(); - - let found = parse_optional_nested_meta_items( - &*attrs, - "object_interface_dynamic", - &mut [&mut plugin_type, &mut lazy_registration], - )?; - - let register_object_interface = match found { - None => register_object_interface_as_static(&crate_ident, self_ty_as_ident), - Some(_) => { - // remove attribute 'object_interface_dynamic' from the attribute list because it is not a real proc_macro_attribute - attrs.retain(|attr| !attr.path().is_ident("object_interface_dynamic")); - let plugin_ty = plugin_type - .value - .map(|p| p.into_token_stream()) - .unwrap_or(quote!(#crate_ident::TypeModule)); - let lazy_registration = lazy_registration.value.map(|b| b.value).unwrap_or_default(); - register_object_interface_as_dynamic( - &crate_ident, - self_ty_as_ident, - plugin_ty, - lazy_registration, - ) - } + &plugin_ty, + dynamic.lazy_registration, + ) + } else { + register_object_interface_as_static(&crate_ident, &self_ty) }; let mut has_prerequisites = false; @@ -76,12 +49,7 @@ pub fn impl_object_interface(input: &mut syn::ItemImpl) -> syn::Result syn::Result TokenStream { // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). diff --git a/glib-macros/src/object_subclass_attribute.rs b/glib-macros/src/object_impl_attributes/subclass.rs similarity index 86% rename from glib-macros/src/object_subclass_attribute.rs rename to glib-macros/src/object_impl_attributes/subclass.rs index 94dee11c60c4..dbabae3ab2d3 100644 --- a/glib-macros/src/object_subclass_attribute.rs +++ b/glib-macros/src/object_impl_attributes/subclass.rs @@ -1,60 +1,34 @@ // Take a look at the license at the top of the repository in the LICENSE file. use heck::ToShoutySnakeCase; -use proc_macro2::{Span, TokenStream}; +use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; -use crate::utils::{parse_optional_nested_meta_items, NestedMetaItem}; - -pub const WRONG_PLACE_MSG: &str = - "This macro should be used on `impl` block for `glib::ObjectSubclass` trait"; - -pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> syn::Result { +pub fn impl_object_subclass(input: super::Input) -> TokenStream { let crate_ident = crate::utils::crate_ident_new(); - let syn::ItemImpl { + let super::Input { attrs, generics, - trait_, + trait_path, self_ty, + unsafety, items, - .. + meta_dynamic, } = input; - let self_ty_as_ident = match &**self_ty { - syn::Type::Path(syn::TypePath { path, .. }) => path.require_ident(), - _ => Err(syn::Error::new( - syn::spanned::Spanned::span(self_ty), - "expected this path to be an identifier", - )), - }?; - - let mut plugin_type = NestedMetaItem::::new("plugin_type").value_required(); - let mut lazy_registration = - NestedMetaItem::::new("lazy_registration").value_required(); - - let found = parse_optional_nested_meta_items( - &*attrs, - "object_subclass_dynamic", - &mut [&mut plugin_type, &mut lazy_registration], - )?; - - let register_object_subclass = match found { - None => register_object_subclass_as_static(&crate_ident, self_ty_as_ident), - Some(_) => { - // remove attribute 'object_subclass_dynamic' from the attribute list because it is not a real proc_macro_attribute - attrs.retain(|attr| !attr.path().is_ident("object_subclass_dynamic")); - let plugin_ty = plugin_type - .value - .map(|p| p.into_token_stream()) - .unwrap_or(quote!(#crate_ident::TypeModule)); - let lazy_registration = lazy_registration.value.map(|b| b.value).unwrap_or_default(); - register_object_subclass_as_dynamic( - &crate_ident, - self_ty_as_ident, - plugin_ty, - lazy_registration, - ) - } + let register_object_subclass = if let Some(dynamic) = meta_dynamic { + let plugin_ty = dynamic + .plugin_type + .map(|p| p.into_token_stream()) + .unwrap_or(quote!(#crate_ident::TypeModule)); + register_object_subclass_as_dynamic( + &crate_ident, + &self_ty, + &plugin_ty, + dynamic.lazy_registration, + ) + } else { + register_object_subclass_as_static(&crate_ident, &self_ty) }; let mut has_new = false; @@ -113,14 +87,9 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> syn::Result;)); - let trait_path = &trait_ - .as_ref() - .ok_or_else(|| syn::Error::new(Span::call_site(), WRONG_PLACE_MSG))? - .1; - - Ok(quote! { + quote! { #(#attrs)* - impl #generics #trait_path for #self_ty { + #unsafety impl #generics #trait_path for #self_ty { #parent_type_opt #interfaces_opt #class_opt @@ -196,7 +165,7 @@ pub fn impl_object_subclass(input: &mut syn::ItemImpl) -> syn::Result TokenStream { // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]).