Skip to content

Commit

Permalink
Merge pull request #1379 from felinira/fina/refactor-object-macros
Browse files Browse the repository at this point in the history
glib-macros: Refactor parsing code of object_subclass/object_interface
  • Loading branch information
sdroege authored May 9, 2024
2 parents 839fc12 + 0670577 commit 11a3692
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 128 deletions.
27 changes: 5 additions & 22 deletions glib-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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::<syn::ItemImpl>(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.
Expand Down Expand Up @@ -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::<syn::ItemImpl>(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
Expand Down
136 changes: 136 additions & 0 deletions glib-macros/src/object_impl_attributes.rs
Original file line number Diff line number Diff line change
@@ -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<syn::Attribute>,
generics: syn::Generics,
trait_path: syn::Path,
self_ty: syn::Ident,
unsafety: Option<syn::token::Unsafe>,
items: Vec<syn::ImplItem>,
meta_dynamic: Option<MetaDynamic>,
}

impl Input {
/// Parse an `#[object_interface]` attribute.
pub fn parse_interface(input: syn::parse::ParseStream) -> syn::Result<Self> {
Self::parse(AttrKind::Interface, input)
}

/// Parse an `#[object_subclass]` attribute.
pub fn parse_subclass(input: syn::parse::ParseStream) -> syn::Result<Self> {
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<Self> {
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<syn::Path>,
lazy_registration: bool,
}

impl MetaDynamic {
/// Parse `#[object_subclass_dynamic]` / `#[object_interface_dynamic]`
fn parse_and_remove(
kind: AttrKind,
attrs: &mut Vec<syn::Attribute>,
) -> syn::Result<Option<Self>> {
let attr_name = match kind {
AttrKind::Interface => "object_interface_dynamic",
AttrKind::Subclass => "object_subclass_dynamic",
};

let mut plugin_type = NestedMetaItem::<syn::Path>::new("plugin_type").value_required();
let mut lazy_registration =
NestedMetaItem::<syn::LitBool>::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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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<TokenStream> {
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::<syn::Path>::new("plugin_type").value_required();
let mut lazy_registration =
NestedMetaItem::<syn::LitBool>::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;
Expand All @@ -76,12 +49,7 @@ pub fn impl_object_interface(input: &mut syn::ItemImpl) -> syn::Result<TokenStre
))
};

let trait_path = &trait_
.as_ref()
.ok_or_else(|| syn::Error::new(Span::call_site(), WRONG_PLACE_MSG))?
.1;

Ok(quote! {
quote! {
#(#attrs)*
#unsafety impl #generics #trait_path for #self_ty {
#prerequisites_opt
Expand All @@ -96,7 +64,7 @@ pub fn impl_object_interface(input: &mut syn::ItemImpl) -> syn::Result<TokenStre
}

#register_object_interface
})
}
}

// Registers the object interface as a static type.
Expand Down Expand Up @@ -124,7 +92,7 @@ fn register_object_interface_as_static(
fn register_object_interface_as_dynamic(
crate_ident: &TokenStream,
self_ty: &syn::Ident,
plugin_ty: TokenStream,
plugin_ty: &TokenStream,
lazy_registration: bool,
) -> TokenStream {
// The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]).
Expand Down
Loading

0 comments on commit 11a3692

Please sign in to comment.