glib-macros-0.20.4/.cargo_vcs_info.json0000644000000001510000000000100133130ustar { "git": { "sha1": "3d32a011adc7c1642d3396ec526d2b2a5af1ca1d" }, "path_in_vcs": "glib-macros" }glib-macros-0.20.4/COPYRIGHT000064400000000000000000000012131046102023000133760ustar 00000000000000The gtk-rs Project is licensed under the MIT license, see the LICENSE file or . Copyrights in the gtk-rs Project project are retained by their contributors. No copyright assignment is required to contribute to the gtk-rs Project project. For full authorship information, see the version control history. This project provides interoperability with various GNOME libraries but doesn't distribute any parts of them. Distributing compiled libraries and executables that link to those libraries may be subject to terms of the GNU LGPL or other licenses. For more information check the license of each GNOME library. glib-macros-0.20.4/Cargo.toml0000644000000036140000000000100113200ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.70" name = "glib-macros" version = "0.20.4" authors = ["The gtk-rs Project Developers"] build = false exclude = ["gir-files/*"] autobins = false autoexamples = false autotests = false autobenches = false description = "Rust bindings for the GLib library, proc macros crate" homepage = "https://gtk-rs.org/" documentation = "https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib_macros/" readme = false keywords = [ "glib", "gtk-rs", "gnome", "GUI", ] license = "MIT" repository = "https://github.com/gtk-rs/gtk-rs-core" [lib] name = "glib_macros" path = "src/lib.rs" proc-macro = true [[test]] name = "clone" path = "tests/clone.rs" [[test]] name = "closure" path = "tests/closure.rs" [[test]] name = "enum_dynamic" path = "tests/enum_dynamic.rs" [[test]] name = "flags_dynamic" path = "tests/flags_dynamic.rs" [[test]] name = "object_subclass_dynamic" path = "tests/object_subclass_dynamic.rs" [[test]] name = "properties" path = "tests/properties.rs" [[test]] name = "test" path = "tests/test.rs" [[test]] name = "value_delegate_derive" path = "tests/value_delegate_derive.rs" [dependencies.heck] version = "0.5" [dependencies.proc-macro-crate] version = "3.1" [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "2.0.68" features = ["full"] [dev-dependencies.glib] version = "0.20" [dev-dependencies.trybuild2] version = "1.2" glib-macros-0.20.4/Cargo.toml.orig000064400000000000000000000012241046102023000147740ustar 00000000000000[package] name = "glib-macros" documentation = "https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib_macros/" description = "Rust bindings for the GLib library, proc macros crate" keywords = ["glib", "gtk-rs", "gnome", "GUI"] authors.workspace = true edition.workspace = true exclude.workspace = true homepage.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true version.workspace = true [dependencies] heck = "0.5" proc-macro2 = "1.0" quote = "1.0" syn = { version = "2.0.68", features = ["full"] } proc-macro-crate = "3.1" [lib] proc-macro = true [dev-dependencies] glib.workspace = true trybuild2 = "1.2" glib-macros-0.20.4/LICENSE000064400000000000000000000020001046102023000131030ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. glib-macros-0.20.4/src/boxed_derive.rs000064400000000000000000000251131046102023000157040ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::{Ident, TokenStream}; use quote::quote; use crate::utils::{crate_ident_new, parse_nested_meta_items, NestedMetaItem}; fn gen_option_to_ptr() -> TokenStream { quote! { match s { ::core::option::Option::Some(s) => ::std::boxed::Box::into_raw(::std::boxed::Box::new(s.clone())), ::core::option::Option::None => ::std::ptr::null_mut(), }; } } fn gen_impl_from_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream { quote! { unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name { type Checker = #crate_ident::value::GenericValueTypeOrNoneChecker; #[inline] unsafe fn from_value(value: &'a #crate_ident::Value) -> Self { let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0); debug_assert!(!ptr.is_null()); *::std::boxed::Box::from_raw(ptr as *mut #name) } } unsafe impl<'a> #crate_ident::value::FromValue<'a> for &'a #name { type Checker = #crate_ident::value::GenericValueTypeOrNoneChecker; #[inline] unsafe fn from_value(value: &'a #crate_ident::Value) -> Self { let ptr = #crate_ident::gobject_ffi::g_value_get_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0); debug_assert!(!ptr.is_null()); &*(ptr as *mut #name) } } } } fn gen_impl_from_value(name: &Ident, crate_ident: &TokenStream) -> TokenStream { quote! { unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name { type Checker = #crate_ident::value::GenericValueTypeChecker; #[inline] unsafe fn from_value(value: &'a #crate_ident::Value) -> Self { let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0); debug_assert!(!ptr.is_null()); *::std::boxed::Box::from_raw(ptr as *mut #name) } } unsafe impl<'a> #crate_ident::value::FromValue<'a> for &'a #name { type Checker = #crate_ident::value::GenericValueTypeChecker; #[inline] unsafe fn from_value(value: &'a #crate_ident::Value) -> Self { let ptr = #crate_ident::gobject_ffi::g_value_get_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0); debug_assert!(!ptr.is_null()); &*(ptr as *mut #name) } } } } fn gen_impl_to_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream { let option_to_ptr = gen_option_to_ptr(); quote! { impl #crate_ident::value::ToValueOptional for #name { #[inline] fn to_value_optional(s: ::core::option::Option<&Self>) -> #crate_ident::Value { let mut value = #crate_ident::Value::for_value_type::(); unsafe { let ptr: *mut #name = #option_to_ptr; #crate_ident::gobject_ffi::g_value_take_boxed( #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, ptr as *mut _ ); } value } } impl #crate_ident::value::ValueTypeOptional for #name { } } } pub fn impl_boxed(input: &syn::DeriveInput) -> syn::Result { let name = &input.ident; let mut gtype_name = NestedMetaItem::::new("name") .required() .value_required(); let mut nullable = NestedMetaItem::::new("nullable").value_optional(); let mut allow_name_conflict = NestedMetaItem::::new("allow_name_conflict").value_optional(); let found = parse_nested_meta_items( &input.attrs, "boxed_type", &mut [&mut gtype_name, &mut nullable, &mut allow_name_conflict], )?; if found.is_none() { return Err(syn::Error::new_spanned( input, "#[derive(glib::Boxed)] requires #[boxed_type(name = \"BoxedTypeName\")]", )); } let gtype_name = gtype_name.value.unwrap(); let nullable = nullable.found || nullable.value.map(|b| b.value()).unwrap_or(false); let allow_name_conflict = allow_name_conflict.found || allow_name_conflict .value .map(|b| b.value()) .unwrap_or(false); let crate_ident = crate_ident_new(); let impl_from_value = if !nullable { gen_impl_from_value(name, &crate_ident) } else { gen_impl_from_value_optional(name, &crate_ident) }; let impl_to_value_optional = if nullable { gen_impl_to_value_optional(name, &crate_ident) } else { quote! {} }; Ok(quote! { impl #crate_ident::subclass::boxed::BoxedType for #name { const NAME: &'static ::core::primitive::str = #gtype_name; const ALLOW_NAME_CONFLICT: bool = #allow_name_conflict; } impl #crate_ident::prelude::StaticType for #name { #[inline] fn static_type() -> #crate_ident::Type { static TYPE: ::std::sync::OnceLock<#crate_ident::Type> = ::std::sync::OnceLock::new(); *TYPE.get_or_init(|| { #crate_ident::subclass::register_boxed_type::<#name>() }) } } impl #crate_ident::value::ValueType for #name { type Type = #name; } impl #crate_ident::value::ToValue for #name { #[inline] fn to_value(&self) -> #crate_ident::Value { unsafe { let ptr: *mut #name = ::std::boxed::Box::into_raw(::std::boxed::Box::new(self.clone())); let mut value = #crate_ident::Value::from_type_unchecked(<#name as #crate_ident::prelude::StaticType>::static_type()); #crate_ident::gobject_ffi::g_value_take_boxed( #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, ptr as *mut _ ); value } } #[inline] fn value_type(&self) -> #crate_ident::Type { <#name as #crate_ident::prelude::StaticType>::static_type() } } impl ::std::convert::From<#name> for #crate_ident::Value { #[inline] fn from(v: #name) -> Self { unsafe { let mut value = #crate_ident::Value::from_type_unchecked(<#name as #crate_ident::prelude::StaticType>::static_type()); #crate_ident::gobject_ffi::g_value_take_boxed( #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, #crate_ident::translate::IntoGlibPtr::<*mut #name>::into_glib_ptr(v) as *mut _, ); value } } } #impl_to_value_optional #impl_from_value unsafe impl #crate_ident::translate::TransparentPtrType for #name {} impl #crate_ident::translate::GlibPtrDefault for #name { type GlibType = *mut #name; } impl #crate_ident::translate::FromGlibPtrBorrow<*const #name> for #name { #[inline] unsafe fn from_glib_borrow(ptr: *const #name) -> #crate_ident::translate::Borrowed { #crate_ident::translate::FromGlibPtrBorrow::from_glib_borrow(ptr as *mut _) } } impl #crate_ident::translate::FromGlibPtrBorrow<*mut #name> for #name { #[inline] unsafe fn from_glib_borrow(ptr: *mut #name) -> #crate_ident::translate::Borrowed { debug_assert!(!ptr.is_null()); #crate_ident::translate::Borrowed::new(std::ptr::read(ptr)) } } impl #crate_ident::translate::FromGlibPtrNone<*const #name> for #name { #[inline] unsafe fn from_glib_none(ptr: *const #name) -> Self { debug_assert!(!ptr.is_null()); (&*ptr).clone() } } impl #crate_ident::translate::FromGlibPtrNone<*mut #name> for #name { #[inline] unsafe fn from_glib_none(ptr: *mut #name) -> Self { #crate_ident::translate::FromGlibPtrNone::from_glib_none(ptr as *const _) } } impl #crate_ident::translate::FromGlibPtrFull<*mut #name> for #name { #[inline] unsafe fn from_glib_full(ptr: *mut #name) -> Self { debug_assert!(!ptr.is_null()); *::std::boxed::Box::from_raw(ptr) } } impl #crate_ident::translate::IntoGlibPtr<*mut #name> for #name { #[inline] unsafe fn into_glib_ptr(self) -> *mut #name { ::std::boxed::Box::into_raw(::std::boxed::Box::new(self)) as *mut _ } } impl<'a> #crate_ident::translate::ToGlibPtr<'a, *const #name> for #name { type Storage = std::marker::PhantomData<&'a Self>; #[inline] fn to_glib_none(&'a self) -> #crate_ident::translate::Stash<'a, *const #name, Self> { #crate_ident::translate::Stash(self as *const #name, std::marker::PhantomData) } #[inline] fn to_glib_full(&self) -> *const #name { ::std::boxed::Box::into_raw(::std::boxed::Box::new(self.clone())) } } impl<'a> #crate_ident::translate::ToGlibPtr<'a, *mut #name> for #name { type Storage = std::marker::PhantomData<&'a Self>; #[inline] fn to_glib_none(&'a self) -> #crate_ident::translate::Stash<'a, *mut #name, Self> { #crate_ident::translate::Stash(self as *const #name as *mut _, std::marker::PhantomData) } #[inline] fn to_glib_full(&self) -> *mut #name { ::std::boxed::Box::into_raw(::std::boxed::Box::new(self.clone())) as *mut _ } } impl #crate_ident::prelude::HasParamSpec for #name { type ParamSpec = #crate_ident::ParamSpecBoxed; type SetValue = Self; type BuilderFn = fn(&::core::primitive::str) -> #crate_ident::ParamSpecBoxedBuilder; fn param_spec_builder() -> Self::BuilderFn { |name| Self::ParamSpec::builder(name) } } }) } glib-macros-0.20.4/src/clone.rs000064400000000000000000000452531046102023000143540ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{ parse::{Parse, ParseStream}, spanned::Spanned, Attribute, Expr, ExprAsync, ExprClosure, Token, }; use crate::utils::crate_ident_new; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) enum CaptureKind { Watch, Weak, WeakAllowNone, Strong, ToOwned, } impl<'a> TryFrom<&'a Ident> for CaptureKind { type Error = syn::Error; fn try_from(s: &Ident) -> Result { Ok(match s.to_string().as_str() { "watch" => CaptureKind::Watch, "strong" => CaptureKind::Strong, "weak" => CaptureKind::Weak, "weak_allow_none" => CaptureKind::WeakAllowNone, "to_owned" => CaptureKind::ToOwned, _ => { // This is actually never shown to the user but we need some kind of error type for // TryFrom, () would be enough but then clippy complains. // // We'll keep it here in case it is useful somewhere at a later time. return Err(syn::Error::new( s.span(), format!("unknown capture type `{s}`"), )); } }) } } #[derive(Default)] pub(crate) enum UpgradeBehaviour { #[default] Unit, Panic, Default, Expression(Expr), Closure(ExprClosure), } impl UpgradeBehaviour { pub(crate) fn maybe_parse( attrs: &[Attribute], input: ParseStream, ) -> syn::Result> { // Caller checked for empty let attr = &attrs[0]; attr.meta.require_path_only()?; let Some(attr_name) = attr.path().get_ident() else { return Ok(None); }; let upgrade_behaviour = match attr_name.to_string().as_str() { "upgrade_or" => { let expr = input.parse::()?; input.parse::()?; UpgradeBehaviour::Expression(expr) } "upgrade_or_else" => { let closure = input.parse::()?; if closure.asyncness.is_some() { return Err(syn::Error::new_spanned( &closure, "`upgrade_or_else` closure needs to be a non-async closure", )); } if !closure.inputs.is_empty() { return Err(syn::Error::new_spanned( &closure, "`upgrade_or_else` closure must not have any parameters", )); } input.parse::()?; UpgradeBehaviour::Closure(closure) } "upgrade_or_default" => UpgradeBehaviour::Default, "upgrade_or_panic" => UpgradeBehaviour::Panic, _ => { return Ok(None); } }; if attrs.len() > 1 { return Err(syn::Error::new_spanned( &attrs[1], format!( "upgrade failure attribute must not be followed by any other attributes. Found {} more attribute{}", attrs.len() - 1, if attrs.len() > 2 { "s" } else { "" }, ))); } let next_attrs = &input.call(Attribute::parse_outer)?; if !next_attrs.is_empty() { return Err(syn::Error::new_spanned( &next_attrs[0], format!( "upgrade failure attribute must not be followed by any other attributes. Found {} more attribute{}", next_attrs.len(), if next_attrs.len() > 1 { "s" } else { "" }, ) )); } Ok(Some(upgrade_behaviour)) } } pub(crate) struct Capture { pub(crate) name: Expr, pub(crate) alias: Option, pub(crate) kind: CaptureKind, } impl Capture { pub(crate) fn maybe_parse( attrs: &[Attribute], input: ParseStream, ) -> syn::Result> { // Caller checked for empty let attr = &attrs[0]; let Some(attr_name) = attr.path().get_ident() else { return Ok(None); }; let Ok(kind) = CaptureKind::try_from(attr_name) else { return Ok(None); }; if attrs.len() > 1 { return Err(syn::Error::new_spanned( &attrs[1], "variable capture attributes must be followed by an identifier", )); } let mut alias = None; if let syn::Meta::List(ref list) = attr.meta { list.parse_nested_meta(|meta| { if meta.path.is_ident("rename_to") { let value = meta.value()?; let id = value.parse::()?; if alias.is_some() { return Err(meta.error("multiple `rename_to` properties are not allowed")); } alias = Some(id); } else if let Some(ident) = meta.path.get_ident() { return Err( meta.error( format!( "unsupported capture attribute property `{ident}`: only `rename_to` is supported" ), ), ); } else { return Err(meta.error("unsupported capture attribute property")); } Ok(()) })?; } let name = input.parse::()?; match name { Expr::Path(ref p) if p.path.get_ident().is_some() => { if p.path.get_ident().unwrap() == "self" && alias.is_none() { return Err( syn::Error::new_spanned( attr, "capture attribute for `self` requires usage of the `rename_to` attribute property", ), ); } // Nothing to do, it's just an identifier } _ if alias.is_some() => { // Nothing to do, it's an alias } _ => { return Err( syn::Error::new_spanned( attr, "capture attribute for an expression requires usage of the `rename_to` attribute property", ), ); } } input.parse::()?; Ok(Some(Capture { name, alias, kind })) } pub(crate) fn alias(&self) -> TokenStream { if let Some(ref alias) = self.alias { alias.to_token_stream() } else { self.name.to_token_stream() } } pub(crate) fn outer_before_tokens(&self, crate_ident: &TokenStream) -> TokenStream { let alias = self.alias(); let name = &self.name; match self.kind { CaptureKind::Watch => quote! { let #alias = #crate_ident::object::Watchable::watched_object(&#name); }, CaptureKind::Weak | CaptureKind::WeakAllowNone => quote! { let #alias = #crate_ident::clone::Downgrade::downgrade(&#name); }, CaptureKind::Strong => quote! { let #alias = #name.clone(); }, CaptureKind::ToOwned => quote! { let #alias = ::std::borrow::ToOwned::to_owned(&*#name); }, } } pub(crate) fn outer_after_tokens( &self, crate_ident: &TokenStream, closure_ident: &Ident, ) -> TokenStream { let name = &self.name; match self.kind { CaptureKind::Watch => quote! { #crate_ident::object::Watchable::watch_closure(&#name, &#closure_ident); }, _ => Default::default(), } } pub(crate) fn inner_before_tokens( &self, crate_ident: &TokenStream, weak_upgrade_failure_kind: &UpgradeBehaviour, upgrade_failure_closure_ident: &Ident, unit_return: Option, ) -> TokenStream { let alias = self.alias(); match self.kind { CaptureKind::Watch => { quote! { let #alias = unsafe { #alias.borrow() }; let #alias = ::core::convert::AsRef::as_ref(&#alias); } } CaptureKind::Weak => match weak_upgrade_failure_kind { UpgradeBehaviour::Panic => { let err_msg = format!( "Failed to upgrade `{}`. If you don't want to panic, use `#[upgrade_or]`, `#[upgrade_or_else]` or `#[upgrade_or_default]`", alias, ); quote! { let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else { panic!(#err_msg); }; } } UpgradeBehaviour::Default | UpgradeBehaviour::Expression(_) | UpgradeBehaviour::Closure(_) => { let err_msg = format!("Failed to upgrade `{}`", alias); quote! { let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else { #crate_ident::g_debug!( #crate_ident::CLONE_MACRO_LOG_DOMAIN, #err_msg, ); return (#upgrade_failure_closure_ident)(); }; } } UpgradeBehaviour::Unit => { let err_msg = format!("Failed to upgrade `{}`", alias); let unit_return = unit_return.unwrap_or_else(|| { quote! { return; } }); quote! { let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else { #crate_ident::g_debug!( #crate_ident::CLONE_MACRO_LOG_DOMAIN, #err_msg, ); #unit_return }; } } }, CaptureKind::WeakAllowNone => quote! { let #alias = #crate_ident::clone::Upgrade::upgrade(&#alias); }, _ => Default::default(), } } } #[derive(Clone)] enum ClosureOrAsync { Closure(ExprClosure), Async(ExprAsync), } impl Parse for ClosureOrAsync { fn parse(input: ParseStream) -> syn::Result { let expr = input.parse::()?; match expr { Expr::Async(async_) => { if async_.capture.is_none() { return Err(syn::Error::new_spanned( async_, "async blocks need to capture variables by move. Please add the `move` keyword", )); } Ok(ClosureOrAsync::Async(async_)) } Expr::Closure(closure) => { if closure.capture.is_none() { return Err(syn::Error::new_spanned( closure, "closures need to capture variables by move. Please add the `move` keyword", )); } Ok(ClosureOrAsync::Closure(closure)) } _ => Err(syn::Error::new_spanned( expr, "only closures and async blocks are supported", )), } } } impl ToTokens for ClosureOrAsync { fn to_tokens(&self, tokens: &mut TokenStream) { match self { ClosureOrAsync::Closure(ref c) => c.to_tokens(tokens), ClosureOrAsync::Async(ref a) => a.to_tokens(tokens), } } } struct Clone { captures: Vec, upgrade_behaviour: UpgradeBehaviour, body: ClosureOrAsync, } impl Parse for Clone { fn parse(input: ParseStream) -> syn::Result { let mut captures: Vec = vec![]; let mut upgrade_behaviour: Option<(UpgradeBehaviour, Span)> = None; loop { // There must either be one or no attributes here. Multiple attributes are not // supported. // // If this is a capture attribute, it must be followed by an identifier. // If this is an upgrade failure attribute, it might be followed by a closure. After the // upgrade failure attribute there must not be any further attributes. // // If this is not an attribute then it is a closure, async closure or async block which // is handled outside the loop let attrs = input.call(Attribute::parse_outer)?; if attrs.is_empty() { break; }; if let Some(capture) = Capture::maybe_parse(&attrs, input)? { if capture.kind == CaptureKind::Watch { return Err(syn::Error::new_spanned( &attrs[0], "watch variable captures are not supported", )); } captures.push(capture); } else if let Some(behaviour) = UpgradeBehaviour::maybe_parse(&attrs, input)? { if upgrade_behaviour.is_some() { return Err(syn::Error::new_spanned( &attrs[0], "multiple upgrade failure attributes are not supported", )); } upgrade_behaviour = Some((behaviour, attrs[0].span())); break; } else if let Some(ident) = attrs[0].path().get_ident() { return Err(syn::Error::new_spanned( &attrs[0], format!( "unsupported attribute `{ident}`: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported", ), )); } else { return Err(syn::Error::new_spanned( &attrs[0], "unsupported attribute: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported", )); } } if let Some((_, ref span)) = upgrade_behaviour { if captures.iter().all(|c| c.kind != CaptureKind::Weak) { return Err(syn::Error::new( *span, "upgrade failure attribute can only be used together with weak variable captures", )); } } let upgrade_behaviour = upgrade_behaviour.map(|x| x.0).unwrap_or_default(); // Following is a closure or async block let body = input.parse::()?; // Trailing comma, if any if input.peek(Token![,]) { input.parse::()?; } Ok(Clone { captures, upgrade_behaviour, body, }) } } impl ToTokens for Clone { fn to_tokens(&self, tokens: &mut TokenStream) { let crate_ident = crate_ident_new(); let upgrade_failure_closure_ident = Ident::new("____upgrade_failure_closure", Span::call_site()); let outer_before = self .captures .iter() .map(|c| c.outer_before_tokens(&crate_ident)); let inner_before = self.captures.iter().map(|c| { c.inner_before_tokens( &crate_ident, &self.upgrade_behaviour, &upgrade_failure_closure_ident, None, ) }); let upgrade_failure_closure = match self.upgrade_behaviour { UpgradeBehaviour::Default => Some(quote! { let #upgrade_failure_closure_ident = ::std::default::Default::default; }), UpgradeBehaviour::Expression(ref expr) => Some(quote! { let #upgrade_failure_closure_ident = move || { #expr }; }), UpgradeBehaviour::Closure(ref closure) => Some(quote! { let #upgrade_failure_closure_ident = #closure; }), _ => None, }; let body = match self.body { ClosureOrAsync::Closure(ref c) => { let ExprClosure { attrs, lifetimes, constness, movability, asyncness, capture, or1_token, inputs, or2_token, output, body, } = c; quote! { #(#attrs)* #lifetimes #constness #movability #asyncness #capture #or1_token #inputs #or2_token #output { #upgrade_failure_closure #(#inner_before)* #body } } } ClosureOrAsync::Async(ref a) => { let ExprAsync { attrs, async_token, capture, block, } = a; // Directly output the statements instead of the whole block including braces as we // already produce a block with braces below and otherwise a compiler warning about // unnecessary braces is wrongly emitted. let stmts = &block.stmts; quote! { #(#attrs)* #async_token #capture { #upgrade_failure_closure #(#inner_before)* #(#stmts)* } } } }; tokens.extend(quote! { { #(#outer_before)* #body } }); } } pub(crate) fn clone_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let clone = syn::parse_macro_input!(input as Clone); clone.into_token_stream().into() } glib-macros-0.20.4/src/clone_old.rs000064400000000000000000000755531046102023000152200ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use std::iter::Peekable; use proc_macro::{ token_stream::IntoIter as ProcIter, Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree, }; use crate::utils::crate_ident_new; struct PeekableProcIter { inner: Peekable, current_span: Option, next_span: Option, } impl<'a> From<&'a Group> for PeekableProcIter { fn from(f: &'a Group) -> Self { let current_span = Some(f.span()); let mut inner = f.stream().into_iter().peekable(); let next_span = inner.peek().map(|n| n.span()); Self { inner, current_span, next_span, } } } impl From for PeekableProcIter { fn from(f: TokenStream) -> Self { let mut inner = f.into_iter().peekable(); let next_span = inner.peek().map(|n| n.span()); Self { inner, current_span: None, next_span, } } } impl Iterator for PeekableProcIter { type Item = TokenTree; fn next(&mut self) -> Option { let next = self.inner.next()?; self.current_span = Some(next.span()); self.next_span = self.inner.peek().map(|n| n.span()); Some(next) } } impl PeekableProcIter { fn peek(&mut self) -> Option<&TokenTree> { self.inner.peek() } fn generate_error(&self, message: &str) -> TokenStream { self.generate_error_with_span(message, self.current_span) } fn generate_error_with_next_span(&self, message: &str) -> TokenStream { self.generate_error_with_span(message, self.next_span) } fn generate_error_with_span(&self, message: &str, span: Option) -> TokenStream { let span = span.unwrap_or_else(Span::call_site); // We generate a `compile_error` macro and assign it to the current span so the error // displayed by rustc points to the right location. let mut stream = TokenStream::new(); stream.extend(vec![TokenTree::Literal(Literal::string(message))]); let mut tokens = vec![ TokenTree::Ident(Ident::new("compile_error", span)), TokenTree::Punct(Punct::new('!', Spacing::Alone)), TokenTree::Group(Group::new(Delimiter::Parenthesis, stream)), TokenTree::Punct(Punct::new(';', Spacing::Alone)), ]; for tok in &mut tokens { tok.set_span(span); } let mut stream = TokenStream::new(); stream.extend(tokens); let mut t = TokenTree::Group(Group::new(Delimiter::Brace, stream)); t.set_span(span); let mut stream = TokenStream::new(); stream.extend(vec![t]); stream } } #[derive(Clone, Copy, Debug)] enum BorrowKind { Weak, WeakAllowNone, Strong, ToOwned, } impl BorrowKind { fn to_str(self) -> &'static str { match self { Self::Weak => "@weak", Self::WeakAllowNone => "@weak-allow-none", Self::Strong => "@strong", Self::ToOwned => "@to-owned", } } } enum WrapperKind { DefaultPanic, DefaultReturn(String), } impl WrapperKind { fn to_str(&self) -> String { match *self { Self::DefaultPanic => "@default-panic".to_owned(), Self::DefaultReturn(ref r) => format!("@default-return {r}"), } } fn keyword(&self) -> &'static str { match *self { Self::DefaultPanic => "default-panic", Self::DefaultReturn(_) => "default-return", } } } #[derive(Debug)] struct ElemToClone { name: String, alias: Option, borrow_kind: BorrowKind, } impl ElemToClone { fn to_str_before(&self) -> String { match self.borrow_kind { BorrowKind::Weak | BorrowKind::WeakAllowNone => format!( "let {} = {}::clone::Downgrade::downgrade(&{});", if let Some(ref a) = self.alias { a } else { &self.name }, crate_ident_new(), self.name, ), BorrowKind::Strong => format!( "let {} = {}.clone();", if let Some(ref a) = self.alias { a } else { &self.name }, self.name, ), BorrowKind::ToOwned => format!( "let {} = ::std::borrow::ToOwned::to_owned(&*{});", if let Some(ref a) = self.alias { a } else { &self.name }, self.name, ), } } fn to_str_after(&self, wrapper_kind: &Option) -> String { let name = if let Some(ref a) = self.alias { a } else { &self.name }; match (self.borrow_kind, wrapper_kind) { (BorrowKind::Weak, Some(WrapperKind::DefaultPanic)) => { format!( "\ let {0} = match {1}::clone::Upgrade::upgrade(&{0}) {{ Some(val) => val, None => panic!( \"failed to upgrade `{0}` (if you don't want to panic, use @default-return)\", ), }};", name, crate_ident_new(), ) } (BorrowKind::Weak, Some(WrapperKind::DefaultReturn(ref r))) => { let not_unit_ret = r.chars().any(|c| c != '(' && c != ')' && c != ' '); format!( "\ let {0} = match {1}::clone::Upgrade::upgrade(&{0}) {{ Some(val) => val, None => {{ {1}::g_debug!( {1}::CLONE_MACRO_LOG_DOMAIN, \"Failed to upgrade {0}\", ); let ___return_value = || {{ {2} }}; return ___return_value(); }} }};", name, crate_ident_new(), if not_unit_ret { r } else { "" }, ) } (BorrowKind::Weak, None) => { format!( "\ let {0} = match {1}::clone::Upgrade::upgrade(&{0}) {{ Some(val) => val, None => {{ {1}::g_debug!( {1}::CLONE_MACRO_LOG_DOMAIN, \"Failed to upgrade {0}\", ); return; }} }};", name, crate_ident_new(), ) } (BorrowKind::WeakAllowNone, _) => format!( "let {0} = {1}::clone::Upgrade::upgrade(&{0});", name, crate_ident_new(), ), _ => String::new(), } } } enum SimpleToken { Punct(&'static str), Ident(&'static str), } impl SimpleToken { fn to_str(&self) -> &str { match *self { Self::Punct(p) => p, Self::Ident(i) => i, } } } impl PartialEq for SimpleToken { fn eq(&self, other: &TokenTree) -> bool { match (self, other) { (SimpleToken::Punct(p1), TokenTree::Punct(ref p2)) => *p1 == p2.to_string(), (SimpleToken::Ident(i1), TokenTree::Ident(ref i2)) => *i1 == i2.to_string(), _ => false, } } } fn is_punct(elem: &TokenTree, punct: &str) -> bool { match elem { TokenTree::Punct(ref p) => p.to_string() == punct, _ => false, } } enum TokenCheck { UnexpectedToken(String, String), UnexpectedEnd(String), } fn check_tokens( tokens_to_check: &[SimpleToken], parts: &mut PeekableProcIter, ) -> Result<(), TokenCheck> { let mut tokens = String::new(); for token in tokens_to_check { if let Some(next) = parts.peek() { if token != next { return Err(TokenCheck::UnexpectedToken(tokens, next.to_string())); } tokens.push_str(token.to_str()); parts.next(); } else { return Err(TokenCheck::UnexpectedEnd(tokens)); } } Ok(()) } #[doc(alias = "get_full_ident")] fn full_ident( parts: &mut PeekableProcIter, borrow_kind: BorrowKind, ) -> Result { let mut name = String::new(); let mut prev_is_ident = false; loop { match parts.peek() { Some(TokenTree::Punct(p)) => { let p_s = p.to_string(); if p_s == "," || p_s == "=" { break; } else if p_s == "." { if !prev_is_ident { return Err(parts.generate_error_with_next_span(&format!( "Unexpected `.` after `{}`", borrow_kind.to_str() ))); } prev_is_ident = false; name.push('.'); parts.next(); } else if name.is_empty() { return Err(parts .generate_error_with_next_span(&format!("Expected ident, found `{p_s}`"))); } else { return Err(parts.generate_error_with_next_span(&format!( "Expected ident, found `{p_s}` after `{name}`" ))); } } Some(TokenTree::Ident(i)) => { if prev_is_ident { break; } prev_is_ident = true; name.push_str(&i.to_string()); parts.next(); } Some(x) if name.is_empty() => { let err = format!("Expected ident, found `{x}`"); return Err(parts.generate_error_with_next_span(&err)); } Some(x) => { let err = &format!("Expected ident, found `{x}` after `{name}`"); return Err(parts.generate_error_with_next_span(err)); } None => { return Err(parts.generate_error(&format!("Unexpected end after ident `{name}`"))); } } } if name.is_empty() { if let Some(next) = parts.next() { return Err(parts.generate_error(&format!("Expected ident, found `{next}`"))); } return Err(parts.generate_error("Expected something after, found nothing")); } Ok(name) } #[doc(alias = "get_keyword")] fn keyword(parts: &mut PeekableProcIter) -> Result { let mut ret = String::new(); let mut prev_is_ident = false; let mut stored = false; // We unfortunately can't join spans since the `Span::join` method is nightly-only. Well, we'll // do our best... let start_span = parts.next_span; loop { match parts.peek() { Some(TokenTree::Ident(i)) => { if prev_is_ident { break; } prev_is_ident = true; if stored { ret.push('-'); stored = false; } ret.push_str(&i.to_string()); } Some(TokenTree::Punct(p)) if p.to_string() == "-" => { if !prev_is_ident { break; } // This is to prevent to push `-` if the next item isn't an ident. prev_is_ident = false; stored = true; } _ => break, } parts.next(); } let ret = match ret.as_str() { "strong" => BorrowKind::Strong, "weak" => BorrowKind::Weak, "weak-allow-none" => BorrowKind::WeakAllowNone, "to-owned" => BorrowKind::ToOwned, "default-return" => { return Err(parts .generate_error_with_span("`@default-return` should be after `=>`", start_span)); } "default-panic" => { return Err( parts.generate_error_with_span("`@default-panic` should be after `=>`", start_span) ); } k => { return Err(parts.generate_error_with_span( &format!( "Unknown keyword `{k}`, only `weak`, `weak-allow-none`, `to-owned` and \ `strong` are allowed" ), start_span, )); } }; Ok(ret) } fn parse_ident( parts: &mut PeekableProcIter, elements: &mut Vec, ) -> Result<(), TokenStream> { let borrow_kind = keyword(parts)?; let name = full_ident(parts, borrow_kind)?; let name_span = parts.current_span; if name.ends_with('.') { return Err( parts.generate_error_with_span(&format!("Invalid variable name: `{name}`"), name_span) ); } let alias = match parts.peek() { Some(TokenTree::Ident(p)) if p.to_string() == "as" => { parts.next(); let current_span = parts.current_span; match parts.next() { Some(TokenTree::Ident(i)) => Some(i.to_string()), Some(x) => { let err = format!("Expected ident after `as` keyword, found `{x}`"); return Err(parts.generate_error(&err)); } None => { return Err(parts.generate_error_with_span( "Unexpected end after `as` keyword", current_span, )) } } } Some(TokenTree::Ident(p)) => { let err = format!("Unexpected `{p}`"); return Err(parts.generate_error(&err)); } _ => None, }; if name == "self" && alias.is_none() { return Err(parts.generate_error_with_span( "Can't use `self` as variable name. Try storing it in a temporary variable or \ rename it using `as`.", name_span, )); } else if name.contains('.') && alias.is_none() { let err = format!("`{name}`: Field accesses are not allowed as is, you must rename it!"); return Err(parts.generate_error_with_span(&err, name_span)); } elements.push(ElemToClone { name, alias, borrow_kind, }); Ok(()) } fn delimiter_to_string(delimiter: Delimiter, open: bool) -> &'static str { match delimiter { Delimiter::Parenthesis => { if open { "(" } else { ")" } } Delimiter::Brace => { if open { "{" } else { "}" } } Delimiter::Bracket => { if open { "[" } else { "]" } } Delimiter::None => "", } } fn group_to_string(g: &Group) -> String { format!( "{}{}{}", delimiter_to_string(g.delimiter(), true), tokens_to_string(PeekableProcIter::from(g)), delimiter_to_string(g.delimiter(), false), ) } #[doc(alias = "get_expr")] fn expr(parts: &mut PeekableProcIter) -> Result { let mut ret = String::new(); let mut total = 0; let span = parts.current_span; match parts.next() { Some(TokenTree::Literal(l)) => ret.push_str(&l.to_string()), Some(TokenTree::Ident(i)) => ret.push_str(&i.to_string()), Some(TokenTree::Punct(p)) => match p.to_string().as_str() { "[" | "{" | "(" => { total += 1; } x => { return Err(parts .generate_error(&format!("Unexpected token `{x}` after `@default-return`"))) } }, Some(TokenTree::Group(g)) => return Ok(group_to_string(&g)), None => { return Err( parts.generate_error_with_span("Unexpected end after `@default-return`", span) ) } }; loop { match parts.peek() { Some(TokenTree::Punct(p)) => { let p_s = p.to_string(); if p_s == "{" || p_s == "(" || p_s == "[" || p_s == "<" { total += 1; } else if p_s == "}" || p_s == ")" || p_s == "]" || p_s == ">" { total -= 1; } else if p_s == "," && total == 0 { return Ok(ret); } ret.push_str(&p_s); } Some(TokenTree::Group(g)) => { ret.push_str(&group_to_string(g)); } Some(x) => { if total == 0 && !ret.ends_with(':') { return Ok(ret); } ret.push_str(&x.to_string()) } None => return Err(parts.generate_error( "Unexpected end after `{ret}`. Did you forget a `,` after the @default-return value?", )), } parts.next(); } } #[doc(alias = "get_return_kind")] fn return_kind(parts: &mut PeekableProcIter) -> Result { match check_tokens( &[SimpleToken::Ident("default"), SimpleToken::Punct("-")], parts, ) { Err(TokenCheck::UnexpectedToken(tokens, unexpected_token)) => { return Err( parts.generate_error(&format!("Unknown keyword `{tokens}{unexpected_token}`")) ); } Err(TokenCheck::UnexpectedEnd(tokens)) => { return Err(parts.generate_error(&format!("Unexpected end after tokens `{tokens}`"))); } Ok(()) => {} } let prev = parts.current_span; match parts.next() { Some(TokenTree::Ident(i)) => { let i_s = i.to_string(); if i_s == "panic" { return Ok(WrapperKind::DefaultPanic); } assert!(i_s == "return", "Unknown keyword `@default-{i_s}`"); } Some(x) => { let err = format!("Unknown token `{x}` after `@default-`"); return Err(parts.generate_error(&err)); } None => { return Err(parts.generate_error_with_span("Unexpected end after `@default-`", prev)) } } Ok(WrapperKind::DefaultReturn(expr(parts)?)) } fn parse_return_kind(parts: &mut PeekableProcIter) -> Result, TokenStream> { match parts.peek() { Some(TokenTree::Punct(p)) if p.to_string() == "@" => {} None => return Err(parts.generate_error("Unexpected end 2")), _ => return Ok(None), } parts.next(); let ret = return_kind(parts)?; match check_tokens(&[SimpleToken::Punct(",")], parts) { Err(TokenCheck::UnexpectedToken(_, unexpected_token)) => { let err = format!( "Expected `,` after `{}`, found `{unexpected_token}`", ret.to_str(), ); return Err(parts.generate_error_with_next_span(&err)); } Err(TokenCheck::UnexpectedEnd(tokens)) => { let err = format!("Expected `,` after `{}{tokens}`", ret.to_str()); return Err(parts.generate_error(&err)); } Ok(()) => {} } Ok(Some(ret)) } enum BlockKind { Closure(Vec), ClosureWrappingAsync(Vec), AsyncClosure(Vec), AsyncBlock, } impl BlockKind { #[doc(alias = "get_closure")] fn closure(self) -> Option> { match self { Self::AsyncBlock => None, Self::Closure(c) | Self::ClosureWrappingAsync(c) | Self::AsyncClosure(c) => Some(c), } } } fn check_move_after_async(parts: &mut PeekableProcIter) -> Result<(), TokenStream> { let span = parts.current_span; match parts.next() { Some(TokenTree::Ident(i)) if i.to_string() == "move" => Ok(()), // The next checks are just for better error messages. Some(TokenTree::Ident(i)) => { let err = format!("Expected `move` after `async`, found `{i}`"); Err(parts.generate_error(&err)) } Some(TokenTree::Punct(p)) => { let err = format!("Expected `move` after `async`, found `{p}`"); Err(parts.generate_error(&err)) } Some(TokenTree::Group(g)) => { let err = format!( "Expected `move` after `async`, found `{}`", delimiter_to_string(g.delimiter(), true), ); Err(parts.generate_error(&err)) } _ => Err(parts.generate_error_with_span("Expected `move` after `async`", span)), } } fn check_async_syntax(parts: &mut PeekableProcIter) -> Result { check_move_after_async(parts)?; match parts.peek() { Some(TokenTree::Punct(p)) if p.to_string() == "|" => { parts.next(); Ok(BlockKind::AsyncClosure(closure(parts)?)) } Some(TokenTree::Punct(p)) => { let err = format!("Expected closure or block after `async move`, found `{p}`"); Err(parts.generate_error_with_next_span(&err)) } Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => Ok(BlockKind::AsyncBlock), Some(TokenTree::Group(g)) => { let err = format!( "Expected closure or block after `async move`, found `{}`", delimiter_to_string(g.delimiter(), true), ); Err(parts.generate_error_with_next_span(&err)) } _ => Err(parts.generate_error("Expected closure or block after `async move`")), } } // Returns `true` if this is an async context. fn check_before_closure(parts: &mut PeekableProcIter) -> Result { let is_async = match parts.peek() { Some(TokenTree::Ident(i)) if i.to_string() == "move" => false, Some(TokenTree::Ident(i)) if i.to_string() == "async" => true, Some(TokenTree::Ident(i)) if i.to_string() == "default" => { let span = parts.next_span; let ret = return_kind(parts)?; let err = format!("Missing `@` before `{}`", ret.keyword()); return Err(parts.generate_error_with_span(&err, span)); } Some(TokenTree::Punct(p)) if p.to_string() == "|" => { return Err(parts.generate_error_with_next_span( "Closure needs to be \"moved\" so please add `move` before closure", )); } _ => { return Err( parts.generate_error_with_next_span("Missing `move` and closure declaration") ) } }; parts.next(); if is_async { return check_async_syntax(parts); } match parts.peek() { Some(TokenTree::Punct(p)) if p.to_string() == "|" => {} Some(x) => { let err = format!("Expected closure, found `{x}`"); return Err(parts.generate_error_with_next_span(&err)); } None => return Err(parts.generate_error("Expected closure")), } parts.next(); let closure = closure(parts)?; match parts.peek() { Some(TokenTree::Ident(i)) if i.to_string() == "async" => { parts.next(); check_move_after_async(parts)?; match parts.peek() { Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => { Ok(BlockKind::ClosureWrappingAsync(closure)) } // The next matchings are for better error messages. Some(TokenTree::Punct(p)) => { let err = format!("Expected block after `| async move`, found `{p}`"); Err(parts.generate_error_with_next_span(&err)) } Some(TokenTree::Group(g)) => { let err = format!( "Expected block after `| async move`, found `{}`", delimiter_to_string(g.delimiter(), true), ); Err(parts.generate_error_with_next_span(&err)) } _ => { Err(parts.generate_error_with_next_span("Expected block after `| async move`")) } } } _ => Ok(BlockKind::Closure(closure)), } } #[doc(alias = "get_closure")] fn closure(parts: &mut PeekableProcIter) -> Result, TokenStream> { let mut ret = Vec::new(); loop { let span = parts.current_span; match parts.next() { Some(TokenTree::Punct(p)) if p.to_string() == "|" => break, Some(x) => ret.push(x), None => return Err(parts.generate_error_with_span("Unexpected end 3", span)), } } Ok(ret) } fn tokens_to_string(parts: impl Iterator) -> String { let mut ret = String::new(); // This is used in case of "if ident" or other similar cases. let mut prev_is_ident = false; let handle_ident_like = |i: String, ret: &mut String, prev_is_ident: &mut bool| { if *prev_is_ident { ret.push(' '); } ret.push_str(&i); *prev_is_ident = true; }; for token in parts { match token { TokenTree::Punct(p) => { prev_is_ident = false; ret.push_str(&p.to_string()); } TokenTree::Ident(i) => handle_ident_like(i.to_string(), &mut ret, &mut prev_is_ident), TokenTree::Literal(l) => handle_ident_like(l.to_string(), &mut ret, &mut prev_is_ident), TokenTree::Group(g) => { prev_is_ident = false; ret.push_str(&group_to_string(&g)); } } } ret } fn build_closure( parts: PeekableProcIter, elements: Vec, return_kind: Option, kind: BlockKind, ) -> TokenStream { let mut body = TokenStream::new(); for el in &elements { let stream: TokenStream = el .to_str_after(&return_kind) .parse() .expect("failed to convert element after"); body.extend(stream.into_iter().collect::>()); } body.extend(parts.collect::>()); // To prevent to lose the spans in case some errors occur in the code, we need to keep `body`! // // If we replaced everything that follows with a `format!`, it'd look like this: // // format!( // "{{\n{}\nmove |{}| {{\n{}\nlet ____ret = {{ {} }};\n____ret\n}}\n}}", // elements // .iter() // .map(|x| x.to_str_before()) // .collect::>() // .join("\n"), // closure, // elements // .iter() // .map(|x| x.to_str_after(&return_kind)) // .collect::>() // .join("\n"), // body, // ) let mut ret: Vec = vec![]; for el in elements { let stream: TokenStream = el .to_str_before() .parse() .expect("failed to convert element"); ret.extend(stream.into_iter().collect::>()); } // This part is creating the TokenStream using the variables that needs to be cloned (from the // @weak and @strong annotations). let mut inner: Vec = Vec::new(); { let stream = "{ #[deprecated = \"Using old-style clone! syntax\"] macro_rules! clone { () => {}; } clone!(); }" .parse::() .expect("can't parse deprecation"); inner.extend(stream); } if matches!(kind, BlockKind::ClosureWrappingAsync(_)) { inner.extend(vec![ TokenTree::Ident(Ident::new("async", Span::call_site())), TokenTree::Ident(Ident::new("move", Span::call_site())), ]); } let is_async_closure_kind = matches!(kind, BlockKind::AsyncClosure(_)); if let Some(closure) = kind.closure() { if is_async_closure_kind { ret.push(TokenTree::Ident(Ident::new("async", Span::call_site()))); } ret.extend(vec![ TokenTree::Ident(Ident::new("move", Span::call_site())), TokenTree::Punct(Punct::new('|', Spacing::Alone)), ]); ret.extend(closure); ret.extend(vec![TokenTree::Punct(Punct::new('|', Spacing::Alone))]); } else { ret.extend(vec![ TokenTree::Ident(Ident::new("async", Span::call_site())), TokenTree::Ident(Ident::new("move", Span::call_site())), ]); } // The commented lines that follow *might* be useful, don't know. Just in case, I'm keeping // them around. You're welcome future me! inner.extend(vec![ // TokenTree::Ident(Ident::new("let", Span::call_site())), // TokenTree::Ident(Ident::new("____ret", Span::call_site())), // TokenTree::Punct(Punct::new('=', Spacing::Alone)), TokenTree::Group(Group::new(Delimiter::Brace, body)), // TokenTree::Punct(Punct::new(';', Spacing::Alone)), // TokenTree::Ident(Ident::new("____ret", Span::call_site())), ]); let mut inners = TokenStream::new(); inners.extend(inner); ret.extend(vec![TokenTree::Group(Group::new(Delimiter::Brace, inners))]); let mut rets = TokenStream::new(); rets.extend(ret); TokenTree::Group(Group::new(Delimiter::Brace, rets)).into() } pub(crate) fn clone_inner(item: TokenStream) -> TokenStream { let mut parts: PeekableProcIter = item.into(); let mut elements = Vec::new(); let mut prev_is_ident = false; loop { let prev = parts.current_span; match parts.next() { Some(TokenTree::Punct(ref p)) => { let p_s = p.to_string(); if p_s == "=" && parts.peek().map_or_else(|| false, |n| is_punct(n, ">")) { parts.next(); break; } else if p_s == "@" { if let Err(e) = parse_ident(&mut parts, &mut elements) { return e; } prev_is_ident = true; } else if p_s == "," { assert!(prev_is_ident, "Unexpected `,`"); prev_is_ident = false; } else if p_s == "|" { assert!( !elements.is_empty(), "If you have nothing to clone, no need to use this macro!" ); return parts.generate_error("Expected `=>` before closure"); } } Some(TokenTree::Ident(i)) => { let err = format!( "Unexpected ident `{i}`: you need to specify if this is a weak or a strong \ clone.", ); return parts.generate_error(&err); } Some(t) => { let err = format!("Unexpected token `{t}`"); return parts.generate_error(&err); } None => return parts.generate_error_with_span("Unexpected end 4", prev), } } assert!( !elements.is_empty(), "If you have nothing to clone, no need to use this macro!" ); let return_kind = match parse_return_kind(&mut parts) { Ok(r) => r, Err(e) => return e, }; let kind = match check_before_closure(&mut parts) { Ok(r) => r, Err(e) => return e, }; build_closure(parts, elements, return_kind, kind) } glib-macros-0.20.4/src/closure.rs000064400000000000000000000222241046102023000147210ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{ parse::{Parse, ParseStream}, spanned::Spanned, Attribute, ExprClosure, Token, }; use crate::{ clone::{Capture, CaptureKind, UpgradeBehaviour}, utils::crate_ident_new, }; struct Closure { captures: Vec, args: Vec, upgrade_behaviour: UpgradeBehaviour, closure: ExprClosure, constructor: &'static str, } impl Parse for Closure { fn parse(input: ParseStream) -> syn::Result { let mut captures: Vec = vec![]; let mut upgrade_behaviour: Option<(UpgradeBehaviour, Span)> = None; loop { // There must either be one or no attributes here. Multiple attributes are not // supported. // // If this is a capture attribute, it must be followed by an identifier. // If this is an upgrade failure attribute, it might be followed by a closure. After the // upgrade failure attribute there must not be any further attributes. // // If this is not an attribute then it is a closure, async closure or async block which // is handled outside the loop let attrs = input.call(Attribute::parse_outer)?; if attrs.is_empty() { break; }; if let Some(capture) = Capture::maybe_parse(&attrs, input)? { if capture.kind == CaptureKind::Watch && captures.iter().any(|c| c.kind == CaptureKind::Watch) { return Err(syn::Error::new_spanned( &attrs[0], "only one `watch` capture is allowed per closure", )); } captures.push(capture); } else if let Some(behaviour) = UpgradeBehaviour::maybe_parse(&attrs, input)? { if upgrade_behaviour.is_some() { return Err(syn::Error::new_spanned( &attrs[0], "multiple upgrade failure attributes are not supported", )); } upgrade_behaviour = Some((behaviour, attrs[0].span())); break; } else if let Some(ident) = attrs[0].path().get_ident() { return Err(syn::Error::new_spanned( &attrs[0], format!( "unsupported attribute `{ident}`: only `watch`, `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported", ), )); } else { return Err(syn::Error::new_spanned( &attrs[0], "unsupported attribute: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported", )); } } if let Some((_, ref span)) = upgrade_behaviour { if captures.iter().all(|c| c.kind != CaptureKind::Weak) { return Err(syn::Error::new( *span, "upgrade failure attribute can only be used together with weak variable captures", )); } } let upgrade_behaviour = upgrade_behaviour.map(|x| x.0).unwrap_or_default(); let mut closure = input.parse::()?; if closure.asyncness.is_some() { return Err(syn::Error::new_spanned( closure, "async closures not supported", )); } if !captures.is_empty() && closure.capture.is_none() { return Err(syn::Error::new_spanned( closure, "closures need to capture variables by move. Please add the `move` keyword", )); } closure.capture = None; let args = closure .inputs .iter() .enumerate() .map(|(i, _)| Ident::new(&format!("____value{i}"), Span::call_site())) .collect(); // Trailing comma, if any if input.peek(Token![,]) { input.parse::()?; } Ok(Closure { captures, args, upgrade_behaviour, closure, constructor: "new", }) } } impl ToTokens for Closure { fn to_tokens(&self, tokens: &mut TokenStream) { let crate_ident = crate_ident_new(); let closure_ident = Ident::new("____closure", Span::call_site()); let values_ident = Ident::new("____values", Span::call_site()); let upgrade_failure_closure_ident = Ident::new("____upgrade_failure_closure", Span::call_site()); let upgrade_failure_closure_wrapped_ident = Ident::new("____upgrade_failure_closure_wrapped", Span::call_site()); let outer_before = self .captures .iter() .map(|c| c.outer_before_tokens(&crate_ident)); let inner_before = self.captures.iter().map(|c| { c.inner_before_tokens( &crate_ident, &self.upgrade_behaviour, &upgrade_failure_closure_wrapped_ident, Some(quote! { return #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value(()); }), ) }); let outer_after = self .captures .iter() .map(|c| c.outer_after_tokens(&crate_ident, &closure_ident)); let arg_values = self.args.iter().enumerate().map(|(index, arg)| { let err_msg = format!("Wrong type for argument {index}: {{:?}}"); quote! { let #arg = ::core::result::Result::unwrap_or_else( #crate_ident::Value::get(&#values_ident[#index]), |e| panic!(#err_msg, e), ); } }); let arg_names = &self.args; let args_len = self.args.len(); let closure = &self.closure; let constructor = Ident::new(self.constructor, Span::call_site()); let upgrade_failure_closure = match self.upgrade_behaviour { UpgradeBehaviour::Default => Some(quote! { let #upgrade_failure_closure_ident = ::std::default::Default::default; let #upgrade_failure_closure_wrapped_ident = || #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value( (#upgrade_failure_closure_ident)() ); }), UpgradeBehaviour::Expression(ref expr) => Some(quote! { let #upgrade_failure_closure_ident = move || { #expr }; let #upgrade_failure_closure_wrapped_ident = || #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value( (#upgrade_failure_closure_ident)() ); }), UpgradeBehaviour::Closure(ref closure_2) => Some(quote! { let #upgrade_failure_closure_ident = #closure_2; let #upgrade_failure_closure_wrapped_ident = || #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value( (#upgrade_failure_closure_ident)() ); }), _ => None, }; let assert_return_type = upgrade_failure_closure.is_some().then(|| { quote! { fn ____same(_a: &T, _b: impl Fn() -> T) {} ____same(&____res, #upgrade_failure_closure_ident); } }); tokens.extend(quote! { { let #closure_ident = { #(#outer_before)* #crate_ident::closure::RustClosure::#constructor(move |#values_ident| { assert_eq!( #values_ident.len(), #args_len, "Expected {} arguments but got {}", #args_len, #values_ident.len(), ); #upgrade_failure_closure #(#inner_before)* #(#arg_values)* #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value({ let ____res = (#closure)(#(#arg_names),*); #assert_return_type ____res }) } ) }; #(#outer_after)* #closure_ident } }); } } pub(crate) fn closure_inner( input: proc_macro::TokenStream, constructor: &'static str, ) -> proc_macro::TokenStream { let mut closure = syn::parse_macro_input!(input as Closure); closure.constructor = constructor; closure.into_token_stream().into() } glib-macros-0.20.4/src/closure_old.rs000064400000000000000000000234011046102023000155550ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ext::IdentExt, spanned::Spanned, Token}; use crate::utils::crate_ident_new; #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum CaptureKind { Watch, WeakAllowNone, Strong, ToOwned, } struct Capture { name: TokenStream, alias: Option, kind: CaptureKind, start: Span, } impl Capture { fn alias(&self) -> TokenStream { if let Some(ref a) = self.alias { a.to_token_stream() } else { self.name.to_token_stream() } } fn outer_before_tokens(&self, crate_ident: &TokenStream) -> TokenStream { let alias = self.alias(); let name = &self.name; match self.kind { CaptureKind::Watch => quote! { let #alias = #crate_ident::object::Watchable::watched_object(&#name); }, CaptureKind::WeakAllowNone => quote! { let #alias = #crate_ident::clone::Downgrade::downgrade(&#name); }, CaptureKind::Strong => quote! { let #alias = #name.clone(); }, CaptureKind::ToOwned => quote! { let #alias = ::std::borrow::ToOwned::to_owned(&*#name); }, } } fn outer_after_tokens(&self, crate_ident: &TokenStream, closure_ident: &Ident) -> TokenStream { let name = &self.name; match self.kind { CaptureKind::Watch => quote! { #crate_ident::object::Watchable::watch_closure(&#name, &#closure_ident); }, _ => Default::default(), } } fn inner_before_tokens(&self, crate_ident: &TokenStream) -> TokenStream { let alias = self.alias(); match self.kind { CaptureKind::Watch => { quote! { let #alias = unsafe { #alias.borrow() }; let #alias = ::core::convert::AsRef::as_ref(&#alias); } } CaptureKind::WeakAllowNone => quote! { let #alias = #crate_ident::clone::Upgrade::upgrade(&#alias); }, _ => Default::default(), } } } impl syn::parse::Parse for CaptureKind { fn parse(input: syn::parse::ParseStream) -> syn::Result { input.parse::()?; let mut idents = TokenStream::new(); idents.append(input.call(syn::Ident::parse_any)?); while input.peek(Token![-]) { input.parse::()?; idents.append(input.call(syn::Ident::parse_any)?); } let keyword = idents .clone() .into_iter() .map(|i| i.to_string()) .collect::>() .join("-"); match keyword.as_str() { "strong" => Ok(CaptureKind::Strong), "watch" => Ok(CaptureKind::Watch), "weak-allow-none" => Ok(CaptureKind::WeakAllowNone), "to-owned" => Ok(CaptureKind::ToOwned), k => Err(syn::Error::new( idents.span(), format!("Unknown keyword `{}`, only `watch`, `weak-allow-none`, `to-owned` and `strong` are allowed", k), )), } } } impl syn::parse::Parse for Capture { fn parse(input: syn::parse::ParseStream) -> syn::Result { let start = input.span(); let kind = input.parse()?; let mut name = TokenStream::new(); name.append(input.call(syn::Ident::parse_any)?); while input.peek(Token![.]) { input.parse::()?; name.append(proc_macro2::Punct::new('.', proc_macro2::Spacing::Alone)); name.append(input.call(syn::Ident::parse_any)?); } let alias = if input.peek(Token![as]) { input.parse::()?; input.parse()? } else { None }; if alias.is_none() { if name.to_string() == "self" { return Err(syn::Error::new_spanned( name, "Can't use `self` as variable name. Try storing it in a temporary variable or \ rename it using `as`.", )); } if name.to_string().contains('.') { return Err(syn::Error::new( name.span(), format!( "`{}`: Field accesses are not allowed as is, you must rename it!", name ), )); } } Ok(Capture { name, alias, kind, start, }) } } struct Closure { captures: Vec, args: Vec, closure: syn::ExprClosure, constructor: &'static str, } impl syn::parse::Parse for Closure { fn parse(input: syn::parse::ParseStream) -> syn::Result { let mut captures: Vec = vec![]; if input.peek(Token![@]) { loop { let capture = input.parse::()?; if capture.kind == CaptureKind::Watch { if let Some(existing) = captures.iter().find(|c| c.kind == CaptureKind::Watch) { return Err(syn::Error::new( existing.start, "Only one `@watch` capture is allowed per closure", )); } } captures.push(capture); if input.peek(Token![,]) { input.parse::()?; if !input.peek(Token![@]) { break; } } else { break; } } } if !captures.is_empty() { input.parse::]>()?; } let mut closure = input.parse::()?; if closure.asyncness.is_some() { return Err(syn::Error::new_spanned( closure, "Async closure not allowed", )); } if !captures.is_empty() && closure.capture.is_none() { return Err(syn::Error::new_spanned( closure, "Closure with captures needs to be \"moved\" so please add `move` before closure", )); } let args = closure .inputs .iter() .enumerate() .map(|(i, _)| Ident::new(&format!("____value{i}"), Span::call_site())) .collect(); closure.capture = None; Ok(Closure { captures, args, closure, constructor: "new", }) } } impl ToTokens for Closure { fn to_tokens(&self, tokens: &mut TokenStream) { let closure_ident = Ident::new("____closure", Span::call_site()); let values_ident = Ident::new("____values", Span::call_site()); let crate_ident = crate_ident_new(); let outer_before = self .captures .iter() .map(|c| c.outer_before_tokens(&crate_ident)); let inner_before = self .captures .iter() .map(|c| c.inner_before_tokens(&crate_ident)); let outer_after = self .captures .iter() .map(|c| c.outer_after_tokens(&crate_ident, &closure_ident)); let arg_values = self.args.iter().enumerate().map(|(index, arg)| { let err_msg = format!("Wrong type for argument {index}: {{:?}}"); quote! { let #arg = ::core::result::Result::unwrap_or_else( #crate_ident::Value::get(&#values_ident[#index]), |e| panic!(#err_msg, e), ); } }); let arg_names = &self.args; let args_len = self.args.len(); let closure = &self.closure; let constructor = Ident::new(self.constructor, Span::call_site()); let deprecated = if self.constructor == "new" { quote! { { #[deprecated = "Using old-style closure! syntax"] macro_rules! closure { () => {}; } closure!(); } } } else { quote! { { #[deprecated = "Using old-style closure_local! syntax"] macro_rules! closure_local { () => {}; } closure_local!(); } } }; tokens.extend(quote! { { let #closure_ident = { #deprecated #(#outer_before)* #crate_ident::closure::RustClosure::#constructor(move |#values_ident| { assert_eq!( #values_ident.len(), #args_len, "Expected {} arguments but got {}", #args_len, #values_ident.len(), ); #(#inner_before)* #(#arg_values)* #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value( (#closure)(#(#arg_names),*) ) }) }; #(#outer_after)* #closure_ident } }); } } pub(crate) fn closure_inner( input: proc_macro::TokenStream, constructor: &'static str, ) -> proc_macro::TokenStream { let mut closure = syn::parse_macro_input!(input as Closure); closure.constructor = constructor; closure.into_token_stream().into() } glib-macros-0.20.4/src/derived_properties_attribute.rs000064400000000000000000000040111046102023000212200ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::{Span, TokenStream}; use quote::quote; pub const WRONG_PLACE_MSG: &str = "This macro should be used on `impl` block for `glib::ObjectImpl` trait"; pub fn impl_derived_properties(input: &syn::ItemImpl) -> syn::Result { let syn::ItemImpl { attrs, generics, trait_, self_ty, items, .. } = input; let trait_path = &trait_ .as_ref() .ok_or_else(|| syn::Error::new(Span::call_site(), WRONG_PLACE_MSG))? .1; let mut has_property = false; let mut has_properties = false; let mut has_set_property = false; for item in items { if let syn::ImplItem::Fn(method) = item { let ident = &method.sig.ident; if ident == "properties" { has_properties = true; } else if ident == "set_property" { has_set_property = true; } else if ident == "property" { has_property = true; } } } let crate_ident = crate::utils::crate_ident_new(); let properties = quote!( fn properties() -> &'static [#crate_ident::ParamSpec] { Self::derived_properties() } ); let set_property = quote!( fn set_property(&self, id: usize, value: &#crate_ident::Value, pspec: &#crate_ident::ParamSpec) { Self::derived_set_property(self, id, value, pspec) } ); let property = quote!( fn property(&self, id: usize, pspec: &#crate_ident::ParamSpec) -> #crate_ident::Value { Self::derived_property(self, id, pspec) } ); let generated = [ (!has_properties).then_some(properties), (!has_set_property).then_some(set_property), (!has_property).then_some(property), ]; Ok(quote!( #(#attrs)* impl #generics #trait_path for #self_ty { #(#items)* #(#generated)* } )) } glib-macros-0.20.4/src/downgrade_derive/enums.rs000064400000000000000000000077161046102023000177150ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro::TokenStream; use quote::{format_ident, quote}; use syn::{Generics, Ident}; use super::fields::{derive_downgrade_fields, DowngradeStructParts}; use crate::utils::crate_ident_new; /// This function derives a weak type for a given strong enum and /// implementations of `Downgrade` and `Upgrade` traits. /// /// # Example /// /// ```rust,ignore /// #[derive(glib::Downgrade)] /// enum Choice { /// This(X, Y), /// That { x: X, y: Y }, /// } /// ``` /// /// Here is what will be derived: /// /// ```rust,ignore /// enum ChoiceWeak { /// This(::Weak, ::Weak), /// That { /// x: ::Weak, /// y: ::Weak, /// }, /// } /// /// impl glib::clone::Downgrade for Choice { /// type Weak = ChoiceWeak; /// /// fn downgrade(&self) -> Self::Weak { /// match self { /// Self::This(ref _0, ref _1) => Self::Weak::This( /// glib::clone::Downgrade::downgrade(_0), /// glib::clone::Downgrade::downgrade(_1), /// ), /// Self::That { ref x, ref y } => Self::Weak::That( /// glib::clone::Downgrade::downgrade(x), /// glib::clone::Downgrade::downgrade(y), /// ), /// } /// } /// } /// /// impl glib::clone::Upgrade for ChoiceWeak { /// type Strong = Choice; /// /// fn upgrade(&self) -> Option { /// Some(match self { /// Self::This(ref _0, ref _1) => Self::Strong::This( /// glib::clone::Upgrade::upgrade(_0)?, /// glib::clone::Upgrade::upgrade(_1)?, /// ), /// Self::That { ref x, ref y } => Self::Strong::That( /// glib::clone::Upgrade::upgrade(x)?, /// glib::clone::Upgrade::upgrade(y)?, /// ), /// }) /// } /// } /// ``` pub fn derive_downgrade_for_enum( ident: Ident, generics: Generics, data_enum: syn::DataEnum, ) -> TokenStream { let glib = crate_ident_new(); let weak_type = format_ident!("{}Weak", ident); let variants: Vec<(Ident, DowngradeStructParts)> = data_enum .variants .into_iter() .map(|variant| (variant.ident, derive_downgrade_fields(variant.fields))) .collect(); let weak_variants: Vec<_> = variants .iter() .map(|(ident, parts)| { let weak_fields = &parts.weak_fields; quote! { #ident #weak_fields } }) .collect(); let downgrade_variants: Vec<_> = variants .iter() .map(|(ident, parts)| { let destruct = &parts.destruct; let downgrade = &parts.downgrade; quote! { Self::#ident #destruct => Self::Weak::#ident #downgrade } }) .collect(); let upgrade_variants: Vec<_> = variants .iter() .map(|(ident, parts)| { let destruct = &parts.destruct; let upgrade = &parts.upgrade; quote! { Self::#ident #destruct => Self::Strong::#ident #upgrade } }) .collect(); let derived = quote! { pub enum #weak_type #generics {#( #weak_variants ),*} impl #generics #glib::clone::Downgrade for #ident #generics { type Weak = #weak_type #generics; fn downgrade(&self) -> Self::Weak { match self {#( #downgrade_variants ),*} } } impl #generics #glib::clone::Upgrade for #weak_type #generics { type Strong = #ident #generics; fn upgrade(&self) -> ::core::option::Option { ::core::option::Option::Some(match self {#( #upgrade_variants ),*}) } } }; derived.into() } glib-macros-0.20.4/src/downgrade_derive/fields.rs000064400000000000000000000131761046102023000200310ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::{Fields, FieldsNamed, FieldsUnnamed, Ident, Type}; use crate::utils::crate_ident_new; /// Parts needed to derive Downgrade and Upgrade implementation. pub struct DowngradeStructParts { /// Inner part of weak type declaration pub weak_fields: TokenStream, /// Term needed to finish declaration. It is usually blank but is `;` for tuple structs. pub end_of_struct: TokenStream, /// Destructuring pattern pub destruct: TokenStream, /// Downgrade code pub downgrade: TokenStream, /// Upgrade code pub upgrade: TokenStream, } /// This function generates parts needed to derive Downgrade and Upgrade /// implementations. /// /// # Example /// /// Let's assume following types are declared. /// /// ```rust,ignore /// struct Unnamed(X, Y); /// /// struct Named { /// x: X, /// y: Y, /// } /// /// enum Choice { /// This(X, Y), /// That { x: X, y: Y }, /// } /// ``` /// /// ## weak_fields /// /// For the struct `Unnamed` and for a enum's variant `Choice::This` /// it will be `(::Weak, ::Weak)`. /// For the struct `Named` and for a enum's variant `Choice::That` /// it will be `{ x: ::Weak, y: ::Weak, }`. /// /// ## end_of_struct /// /// It is a semicolon (`;`) for an `Unnamed` and is blank for the rest. /// /// ## destruct /// /// For the struct `Unnamed` and for a enum's variant `Choice::This` /// it will be `(ref _0, ref _1)`. /// For the struct `Named` and for a enum's variant `Choice::That` /// it will be `{ ref x, ref y }`. /// So it can be used as a destructuring pattern for values of both types, /// strong and weak. /// /// ```rust,ignore /// let Unnamed (ref _0, ref _1) = ; /// let Named { ref x, ref y } = ; /// /// match { /// Choice::This (ref _0, ref _1) => ... , /// Choice::That { ref x, ref y } => ... , /// } /// ``` /// /// # downgrade /// /// ```rust,ignore /// ( /// glib::clone::Downgrade::downgrade(_0), /// glib::clone::Downgrade::downgrade(_1), /// ) /// /// { /// x: glib::clone::Downgrade::downgrade(x), /// y: glib::clone::Downgrade::downgrade(y), /// } /// ``` /// /// # upgrade /// /// ```rust,ignore /// ( /// glib::clone::Upgrade::upgrade(_0)?, /// glib::clone::Upgrade::upgrade(_1)?, /// ) /// /// { /// x: glib::clone::Upgrade::upgrade(x)?, /// y: glib::clone::Upgrade::upgrade(y)?, /// } /// ``` pub fn derive_downgrade_fields(fields: syn::Fields) -> DowngradeStructParts { let glib = crate_ident_new(); match fields { Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { let fields: Vec = unnamed .into_pairs() .map(|pair| pair.into_value()) .map(|field| field.ty) .collect(); let weak_fields: Vec<_> = fields .iter() .map(|ty| { quote! { <#ty as #glib::clone::Downgrade>::Weak } }) .collect(); let field_ident: Vec = (0..fields.len()).map(|i| format_ident!("_{}", i)).collect(); DowngradeStructParts { weak_fields: quote! { (#( #weak_fields ),*) }, end_of_struct: quote!(;), destruct: quote! { (#( ref #field_ident ),*) }, downgrade: quote! { (#( #glib::clone::Downgrade::downgrade(#field_ident) ),*) }, upgrade: quote! { (#( #glib::clone::Upgrade::upgrade(#field_ident)? ),*) }, } } Fields::Named(FieldsNamed { named, .. }) => { let fields: Vec<(Ident, Type)> = named .into_pairs() .map(|pair| pair.into_value()) .map(|field| (field.ident.expect("Field ident is specified"), field.ty)) .collect(); let weak_fields: Vec<_> = fields .iter() .map(|(ident, ty)| { quote! { #ident: <#ty as #glib::clone::Downgrade>::Weak } }) .collect(); let field_ident: Vec<_> = fields.iter().map(|(ident, _ty)| ident).collect(); DowngradeStructParts { weak_fields: quote! { {#( #weak_fields ),*} }, end_of_struct: quote!(), destruct: quote! { {#( ref #field_ident ),*} }, downgrade: quote! { {#( #field_ident: #glib::clone::Downgrade::downgrade(#field_ident) ),*} }, upgrade: quote! { {#( #field_ident: #glib::clone::Upgrade::upgrade(#field_ident)? ),*} }, } } Fields::Unit => DowngradeStructParts { weak_fields: quote! {}, end_of_struct: quote! { ; }, destruct: quote! {}, downgrade: quote! {}, upgrade: quote! {}, }, } } glib-macros-0.20.4/src/downgrade_derive/mod.rs000064400000000000000000000012121046102023000173260ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. mod enums; mod fields; mod structs; use proc_macro::TokenStream; use syn::{Data, DeriveInput}; pub fn impl_downgrade(input: DeriveInput) -> TokenStream { match input.data { Data::Struct(data_struct) => { structs::derive_downgrade_for_struct(input.ident, input.generics, data_struct) } Data::Enum(data_enum) => { enums::derive_downgrade_for_enum(input.ident, input.generics, data_enum) } Data::Union(..) => { panic!("#[derive(Downgrade)] is not available for unions."); } } } glib-macros-0.20.4/src/downgrade_derive/structs.rs000064400000000000000000000063771046102023000202770ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro::TokenStream; use quote::{format_ident, quote}; use syn::{Generics, Ident}; use super::fields::{derive_downgrade_fields, DowngradeStructParts}; use crate::utils::crate_ident_new; /// This function derives a weak type for a given strong struct and /// implementations of `Downgrade` and `Upgrade` traits. /// /// # Example /// /// ```rust,ignore /// #[derive(glib::Downgrade)] /// struct Unnamed(X, Y); /// /// #[derive(glib::Downgrade)] /// struct Named { /// x: X, /// y: Y, /// } /// ``` /// /// Here is what will be derived: /// /// ```rust,ignore /// pub struct UnnamedWeak(::Weak, ::Weak); /// /// impl glib::clone::Downgrade for Unnamed { /// type Weak = UnnamedWeak; /// /// fn downgrade(&self) -> Self::Weak { /// let Self (ref _0, ref _1) = self; /// UnnamedWeak ( /// glib::clone::Downgrade::downgrade(_0), /// glib::clone::Downgrade::downgrade(_1), /// ) /// } /// } /// /// impl glib::clone::Upgrade for UnnamedWeak { /// type Strong = Unnamed; /// /// fn upgrade(&self) -> Option { /// let Self (ref _0, ref _1) = self; /// Some(Unnamed ( /// glib::clone::Upgrade::upgrade(_0)?, /// glib::clone::Upgrade::upgrade(_1)?, /// )) /// } /// } /// /// pub struct NamedWeak { /// x: ::Weak, /// y: ::Weak, /// } /// /// impl glib::clone::Downgrade for Named { /// type Weak = NamedWeak; /// /// fn downgrade(&self) -> Self::Weak { /// let Self { ref x, ref y } = self; /// NamedWeak { /// glib::clone::Downgrade::downgrade(x), /// glib::clone::Downgrade::downgrade(y), /// } /// } /// } /// /// impl glib::clone::Upgrade for NamedWeak { /// type Strong = Named; /// /// fn upgrade(&self) -> Option { /// let Self { ref x, ref y } = self; /// Some(Named { /// glib::clone::Upgrade::upgrade(x)?, /// glib::clone::Upgrade::upgrade(y)?, /// }) /// } /// } /// ``` pub fn derive_downgrade_for_struct( ident: Ident, generics: Generics, data_struct: syn::DataStruct, ) -> TokenStream { let glib = crate_ident_new(); let weak_type = format_ident!("{}Weak", ident); let DowngradeStructParts { weak_fields, end_of_struct, destruct, downgrade, upgrade, } = derive_downgrade_fields(data_struct.fields); let derived = quote! { pub struct #weak_type #generics #weak_fields #end_of_struct impl #generics #glib::clone::Downgrade for #ident #generics { type Weak = #weak_type #generics; fn downgrade(&self) -> Self::Weak { let Self #destruct = self; #weak_type #downgrade } } impl #generics #glib::clone::Upgrade for #weak_type #generics { type Strong = #ident #generics; fn upgrade(&self) -> ::core::option::Option { let Self #destruct = self; ::core::option::Option::Some(#ident #upgrade) } } }; derived.into() } glib-macros-0.20.4/src/enum_derive.rs000064400000000000000000000476331046102023000155620ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use heck::{ToKebabCase, ToShoutySnakeCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned, ToTokens}; use crate::utils::{ crate_ident_new, gen_enum_from_glib, parse_nested_meta_items, parse_optional_nested_meta_items, NestedMetaItem, }; // generates glib::gobject_ffi::GEnumValue structs mapping the enum such as: // glib::gobject_ffi::GEnumValue { // value: Animal::Goat as i32, // value_name: "Goat\0" as *const _ as *const _, // value_nick: "goat\0" as *const _ as *const _, // }, fn gen_enum_values( enum_name: &syn::Ident, enum_variants: &syn::punctuated::Punctuated, ) -> (TokenStream, usize) { let crate_ident = crate_ident_new(); // starts at one as GEnumValue array is null-terminated. let mut n = 1; let recurse = enum_variants.iter().map(|v| { let name = &v.ident; let mut value_name = name.to_string().to_upper_camel_case(); let mut value_nick = name.to_string().to_kebab_case(); let mut name_attr = NestedMetaItem::::new("name").value_required(); let mut nick = NestedMetaItem::::new("nick").value_required(); let found = parse_nested_meta_items(&v.attrs, "enum_value", &mut [&mut name_attr, &mut nick]); if let Err(e) = found { return e.to_compile_error(); } value_name = name_attr.value.map(|s| s.value()).unwrap_or(value_name); value_nick = nick.value.map(|s| s.value()).unwrap_or(value_nick); let value_name = format!("{value_name}\0"); let value_nick = format!("{value_nick}\0"); n += 1; // generates a glib::gobject_ffi::GEnumValue. quote_spanned! {syn::spanned::Spanned::span(&v)=> #crate_ident::gobject_ffi::GEnumValue { value: #enum_name::#name as i32, value_name: #value_name as *const _ as *const _, value_nick: #value_nick as *const _ as *const _, }, } }); ( quote! { #(#recurse)* }, n, ) } pub fn impl_enum(input: &syn::DeriveInput) -> syn::Result { let name = &input.ident; let enum_variants = match input.data { syn::Data::Enum(ref e) => &e.variants, _ => { return Err(syn::Error::new_spanned( input, "#[derive(glib::Enum)] only supports enums", )) } }; let (g_enum_values, nb_enum_values) = gen_enum_values(name, enum_variants); let mut gtype_name = NestedMetaItem::::new("name") .required() .value_required(); let mut allow_name_conflict = NestedMetaItem::::new("allow_name_conflict").value_optional(); let found = parse_nested_meta_items( &input.attrs, "enum_type", &mut [&mut gtype_name, &mut allow_name_conflict], )?; if found.is_none() { return Err(syn::Error::new_spanned( input, "#[derive(glib::Enum)] requires #[enum_type(name = \"EnumTypeName\")]", )); } let gtype_name = gtype_name.value.unwrap(); let allow_name_conflict = allow_name_conflict.found || allow_name_conflict .value .map(|b| b.value()) .unwrap_or(false); 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( &input.attrs, "enum_dynamic", &mut [&mut plugin_type, &mut lazy_registration], )?; let crate_ident = crate_ident_new(); let register_enum = match found { None => register_enum_as_static( &crate_ident, name, gtype_name, allow_name_conflict, g_enum_values, nb_enum_values, ), Some(_) => { if allow_name_conflict { return Err(syn::Error::new_spanned( input, "#[enum_dynamic] and #[enum_type(allow_name_conflict)] are not allowed together", )); } 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_enum_as_dynamic( &crate_ident, plugin_ty, lazy_registration, name, gtype_name, g_enum_values, nb_enum_values, ) } }; let from_glib = gen_enum_from_glib(name, enum_variants); Ok(quote! { impl #crate_ident::translate::IntoGlib for #name { type GlibType = i32; #[inline] fn into_glib(self) -> i32 { self as i32 } } impl #crate_ident::translate::TryFromGlib for #name { type Error = i32; #[inline] unsafe fn try_from_glib(value: i32) -> ::core::result::Result { let from_glib = || { #from_glib }; from_glib().ok_or(value) } } impl #crate_ident::translate::FromGlib for #name { #[inline] unsafe fn from_glib(value: i32) -> Self { use #crate_ident::translate::TryFromGlib; Self::try_from_glib(value).unwrap() } } impl #crate_ident::value::ValueType for #name { type Type = Self; } unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name { type Checker = #crate_ident::value::GenericValueTypeChecker; #[inline] unsafe fn from_value(value: &'a #crate_ident::value::Value) -> Self { #crate_ident::translate::from_glib(#crate_ident::gobject_ffi::g_value_get_enum( #crate_ident::translate::ToGlibPtr::to_glib_none(value).0 )) } } impl #crate_ident::prelude::ToValue for #name { #[inline] fn to_value(&self) -> #crate_ident::value::Value { let mut value = #crate_ident::value::Value::for_value_type::(); unsafe { #crate_ident::gobject_ffi::g_value_set_enum( #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, #crate_ident::translate::IntoGlib::into_glib(*self) ) } value } #[inline] fn value_type(&self) -> #crate_ident::Type { ::static_type() } } impl ::std::convert::From<#name> for #crate_ident::Value { #[inline] fn from(v: #name) -> Self { #crate_ident::value::ToValue::to_value(&v) } } impl #crate_ident::prelude::StaticType for #name { #[inline] fn static_type() -> #crate_ident::Type { Self::register_enum() } } #register_enum impl #crate_ident::HasParamSpec for #name { type ParamSpec = #crate_ident::ParamSpecEnum; type SetValue = Self; type BuilderFn = fn(&::core::primitive::str, Self) -> #crate_ident::ParamSpecEnumBuilder; fn param_spec_builder() -> Self::BuilderFn { |name, default_value| Self::ParamSpec::builder_with_default(name, default_value) } } }) } // Registers the enum as a static type. fn register_enum_as_static( crate_ident: &TokenStream, name: &syn::Ident, gtype_name: syn::LitStr, allow_name_conflict: bool, g_enum_values: TokenStream, nb_enum_values: usize, ) -> TokenStream { let type_name_snippet = if allow_name_conflict { quote! { unsafe { let mut i = 0; loop { let type_name = ::std::ffi::CString::new(if i == 0 { #gtype_name } else { format!("{}-{}", #gtype_name, i) }) .unwrap(); if #crate_ident::gobject_ffi::g_type_from_name(type_name.as_ptr()) == #crate_ident::gobject_ffi::G_TYPE_INVALID { break type_name; } i += 1; } } } } else { quote! { unsafe { let type_name = ::std::ffi::CString::new(#gtype_name).unwrap(); assert_eq!( #crate_ident::gobject_ffi::g_type_from_name(type_name.as_ptr()), #crate_ident::gobject_ffi::G_TYPE_INVALID, "Type {} has already been registered", type_name.to_str().unwrap() ); type_name } } }; // registers the enum on first use (lazy registration). quote! { impl #name { /// Registers the enum only once. #[inline] fn register_enum() -> #crate_ident::Type { static TYPE: ::std::sync::OnceLock<#crate_ident::Type> = ::std::sync::OnceLock::new(); *TYPE.get_or_init(|| { static mut VALUES: [#crate_ident::gobject_ffi::GEnumValue; #nb_enum_values] = [ #g_enum_values #crate_ident::gobject_ffi::GEnumValue { value: 0, value_name: ::std::ptr::null(), value_nick: ::std::ptr::null(), }, ]; let type_name = #type_name_snippet; unsafe { let type_ = #crate_ident::gobject_ffi::g_enum_register_static(type_name.as_ptr(), VALUES.as_ptr()); let type_: #crate_ident::Type = #crate_ident::translate::from_glib(type_); assert!(type_.is_valid()); type_ } }) } } } } // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). // An enum can be reregistered as a dynamic type. fn register_enum_as_dynamic( crate_ident: &TokenStream, plugin_ty: TokenStream, lazy_registration: bool, name: &syn::Ident, gtype_name: syn::LitStr, g_enum_values: TokenStream, nb_enum_values: usize, ) -> TokenStream { // Wrap each GEnumValue to EnumValue let g_enum_values_expr: syn::ExprArray = syn::parse_quote! { [#g_enum_values] }; let enum_values_iter = g_enum_values_expr.elems.iter().map(|v| { quote_spanned! {syn::spanned::Spanned::span(&v)=> #crate_ident::EnumValue::unsafe_from(#v), } }); let enum_values = quote! { #crate_ident::enums::EnumValuesStorage<#nb_enum_values> = unsafe { #crate_ident::enums::EnumValuesStorage::<#nb_enum_values>::new([ #(#enum_values_iter)* ]) } }; // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). // An enum can be reregistered as a dynamic type. if lazy_registration { // registers the enum as a dynamic type on the first use (lazy registration). // a weak reference on the plugin is stored and will be used later on the first use of the enum. // this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the enum has been registered. // the registration status type. let registration_status_type = format_ident!("{}RegistrationStatus", name); // name of the static variable to store the registration status. let registration_status = format_ident!( "{}", registration_status_type.to_string().to_shouty_snake_case() ); // name of the static array to store the enumeration values. let enum_values_array = format_ident!("{}_VALUES", name.to_string().to_shouty_snake_case()); quote! { /// The registration status type: a tuple of the weak reference on the plugin and of the GLib type. struct #registration_status_type(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type); unsafe impl Send for #registration_status_type {} /// The registration status protected by a mutex guarantees so that no other threads are concurrently accessing the data. static #registration_status: ::std::sync::Mutex> = ::std::sync::Mutex::new(None); /// Array of `EnumValue` for the possible enumeration values. static #enum_values_array: #enum_values; impl #name { /// Registers the enum as a dynamic type within the plugin only once. /// Plugin must have been used at least once. /// Do nothing if plugin has never been used or if the enum is already registered as a dynamic type. #[inline] fn register_enum() -> #crate_ident::Type { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so the enum cannot be registered as a dynamic type. None => #crate_ident::Type::INVALID, // plugin has been used and the enum has not been registered yet, so registers it as a dynamic type. Some(#registration_status_type(type_plugin, type_)) if !type_.is_valid() => { *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin.upgrade().unwrap().as_ref(), #gtype_name, #enum_values_array.as_ref()); *type_ }, // plugin has been used and the enum has already been registered as a dynamic type. Some(#registration_status_type(_, type_)) => *type_ } } /// Depending on the plugin lifecycle state and on the registration status of the enum: /// If plugin is used (and has loaded the implementation) for the first time, postpones the registration and stores a weak reference on the plugin. /// If plugin is reused (and has reloaded the implementation) and the enum has been already registered as a dynamic type, reregisters it. /// An enum can be reregistered several times as a dynamic type. /// If plugin is reused (and has reloaded the implementation) and the enum has not been registered yet as a dynamic type, do nothing. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used (this is the first time), so postpones registration of the enum as a dynamic type on the first use. None => { *registration_status = Some(#registration_status_type(#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); true }, // plugin has been used at least one time and the enum has been registered as a dynamic type at least one time, so re-registers it. Some(#registration_status_type(_, type_)) if type_.is_valid() => { *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, #enum_values_array.as_ref()); type_.is_valid() }, // plugin has been used at least one time but the enum has not been registered yet as a dynamic type, so keeps postponed registration. Some(_) => { true } } } /// Depending on the plugin lifecycle state and on the registration status of the enum: /// If plugin has been used (or reused) but the enum has not been registered yet as a dynamic type, cancels the postponed registration by deleting the weak reference on the plugin. /// Else do nothing. #[inline] pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so unload implementation is unexpected. None => false, // plugin has been used at least one time and the enum has been registered as a dynamic type at least one time. Some(#registration_status_type(_, type_)) if type_.is_valid() => true, // plugin has been used at least one time but the enum has not been registered yet as a dynamic type, so cancels the postponed registration. Some(_) => { *registration_status = None; true } } } } } } else { // registers immediately the enum as a dynamic type. // name of the static variable to store the GLib type. let gtype_status = format_ident!("{}_G_TYPE", name.to_string().to_shouty_snake_case()); quote! { /// The GLib type which can be safely shared between threads. static #gtype_status: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); impl #name { /// Do nothing as the enum has been registered on implementation load. #[inline] fn register_enum() -> #crate_ident::Type { let gtype = #gtype_status.load(::std::sync::atomic::Ordering::Acquire); unsafe { <#crate_ident::Type as #crate_ident::translate::FromGlib<#crate_ident::ffi::GType>>::from_glib(gtype) } } /// Registers the enum as a dynamic type within the plugin. /// The enum can be registered several times as a dynamic type. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { static VALUES: #enum_values; let gtype = #crate_ident::translate::IntoGlib::into_glib(<#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, VALUES.as_ref())); #gtype_status.store(gtype, ::std::sync::atomic::Ordering::Release); gtype != #crate_ident::gobject_ffi::G_TYPE_INVALID } /// Do nothing as enums registered as dynamic types are never unregistered. #[inline] pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { true } } } } } glib-macros-0.20.4/src/error_domain_derive.rs000064400000000000000000000040071046102023000172620ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::TokenStream; use quote::quote; use syn::Data; use crate::utils::{crate_ident_new, gen_enum_from_glib, parse_nested_meta_items, NestedMetaItem}; pub fn impl_error_domain(input: &syn::DeriveInput) -> syn::Result { let name = &input.ident; let enum_variants = match input.data { Data::Enum(ref e) => &e.variants, _ => { return Err(syn::Error::new_spanned( input, "#[derive(glib::ErrorDomain)] only supports enums", )) } }; let mut domain_name = NestedMetaItem::::new("name") .required() .value_required(); let found = parse_nested_meta_items(&input.attrs, "error_domain", &mut [&mut domain_name])?; if found.is_none() { return Err(syn::Error::new_spanned( input, "#[derive(glib::ErrorDomain)] requires #[error_domain(name = \"domain-name\")]", )); }; let domain_name = domain_name.value.unwrap(); let crate_ident = crate_ident_new(); let from_glib = gen_enum_from_glib(name, enum_variants); Ok(quote! { impl #crate_ident::error::ErrorDomain for #name { #[inline] fn domain() -> #crate_ident::Quark { use #crate_ident::translate::from_glib; static QUARK: ::std::sync::OnceLock<#crate_ident::Quark> = ::std::sync::OnceLock::new(); *QUARK.get_or_init(|| unsafe { from_glib(#crate_ident::ffi::g_quark_from_static_string(concat!(#domain_name, "\0") as *const ::core::primitive::str as *const _)) }) } #[inline] fn code(self) -> i32 { self as i32 } #[inline] fn from(value: i32) -> ::core::option::Option where Self: ::std::marker::Sized { #from_glib } } }) } glib-macros-0.20.4/src/flags_attribute.rs000064400000000000000000000520171046102023000164270ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use heck::{ToKebabCase, ToShoutySnakeCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{ punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Ident, Variant, Visibility, }; use crate::utils::{ crate_ident_new, parse_nested_meta_items, parse_optional_nested_meta_items, NestedMetaItem, }; pub const WRONG_PLACE_MSG: &str = "#[glib::flags] only supports enums"; pub struct AttrInput { pub enum_name: syn::LitStr, pub allow_name_conflict: bool, } struct FlagsDesc { variant: Variant, name: Option, nick: Option, skip: bool, } impl FlagsDesc { fn from_attrs(variant: Variant, attrs: &[Attribute]) -> syn::Result { let mut name = NestedMetaItem::::new("name").value_required(); let mut nick = NestedMetaItem::::new("nick").value_required(); let mut skip = NestedMetaItem::::new("skip").value_optional(); parse_nested_meta_items(attrs, "flags_value", &mut [&mut name, &mut nick, &mut skip])?; Ok(Self { variant, name: name.value.map(|s| s.value()), nick: nick.value.map(|s| s.value()), skip: skip.found || skip.value.map(|b| b.value()).unwrap_or(false), }) } } // Generate glib::gobject_ffi::GFlagsValue structs mapping the enum such as: // glib::gobject_ffi::GFlagsValue { // value: MyFlags::A.bits(), // value_name: "The Name\0" as *const _ as *const _, // value_nick: "nick\0" as *const _ as *const _, // }, fn gen_flags_values( enum_name: &Ident, enum_variants: &Punctuated, ) -> (TokenStream, usize) { let crate_ident = crate_ident_new(); // start at one as GFlagsValue array is null-terminated let mut n = 1; let recurse = enum_variants .iter() .map(|v| FlagsDesc::from_attrs(v.clone(), &v.attrs).unwrap()) .filter(|desc| !desc.skip) .map(|desc| { let v = desc.variant; let name = &v.ident; let mut value_name = name.to_string().to_upper_camel_case(); let mut value_nick = name.to_string().to_kebab_case(); if let Some(n) = desc.name { value_name = n; } if let Some(n) = desc.nick { value_nick = n; } let value_name = format!("{value_name}\0"); let value_nick = format!("{value_nick}\0"); n += 1; quote_spanned! {v.span()=> #crate_ident::gobject_ffi::GFlagsValue { value: #enum_name::#name.bits(), value_name: #value_name as *const _ as *const _, value_nick: #value_nick as *const _ as *const _, }, } }); ( quote! { #(#recurse)* }, n, ) } fn gen_bitflags( enum_name: &Ident, visibility: &Visibility, enum_variants: &Punctuated, crate_ident: &TokenStream, ) -> TokenStream { let recurse = enum_variants.iter().map(|v| { let name = &v.ident; let disc = v.discriminant.as_ref().expect("missing discriminant"); let value = &disc.1; quote_spanned! {v.span()=> const #name = #value; } }); quote! { #crate_ident::bitflags::bitflags! { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #visibility struct #enum_name: u32 { #(#recurse)* } } } } fn gen_default( enum_name: &Ident, enum_variants: &Punctuated, ) -> Option { enum_variants .iter() .find(|v| v.attrs.iter().any(|attr| attr.path().is_ident("default"))) .map(|v| { let default_value = &v.ident; quote! { impl Default for #enum_name { fn default() -> Self { Self::from_bits_retain(#enum_name::#default_value.bits()) } } } }) } pub fn impl_flags(attr_meta: AttrInput, input: &mut syn::ItemEnum) -> TokenStream { let gtype_name = attr_meta.enum_name; let syn::ItemEnum { attrs, ident: name, vis: visibility, .. } = input; let enum_variants = &input.variants; let (g_flags_values, nb_flags_values) = gen_flags_values(name, enum_variants); let crate_ident = crate_ident_new(); 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, "flags_dynamic", &mut [&mut plugin_type, &mut lazy_registration], ); let register_flags = match found { Err(e) => return e.to_compile_error(), Ok(None) => register_flags_as_static( &crate_ident, name, gtype_name, attr_meta.allow_name_conflict, g_flags_values, nb_flags_values, ), Ok(Some(_)) => { if attr_meta.allow_name_conflict { return syn::Error::new_spanned( input, "#[flags_dynamic] and #[glib::flags(allow_name_conflict)] are not allowed together", ).to_compile_error(); } // remove attribute 'flags_dynamic' from the attribute list because it is not a real proc_macro_attribute attrs.retain(|attr| !attr.path().is_ident("flags_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_flags_as_dynamic( &crate_ident, plugin_ty, lazy_registration, name, gtype_name, g_flags_values, nb_flags_values, ) } }; let bitflags = gen_bitflags(name, visibility, enum_variants, &crate_ident); let default_impl = gen_default(name, enum_variants); quote! { #bitflags #default_impl impl #crate_ident::translate::IntoGlib for #name { type GlibType = u32; #[inline] fn into_glib(self) -> u32 { self.bits() } } impl #crate_ident::translate::FromGlib for #name { #[inline] unsafe fn from_glib(value: u32) -> Self { Self::from_bits_truncate(value) } } impl #crate_ident::value::ValueType for #name { type Type = Self; } unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name { type Checker = #crate_ident::value::GenericValueTypeChecker; #[inline] unsafe fn from_value(value: &'a #crate_ident::value::Value) -> Self { #crate_ident::translate::from_glib(#crate_ident::gobject_ffi::g_value_get_flags( #crate_ident::translate::ToGlibPtr::to_glib_none(value).0 )) } } impl #crate_ident::value::ToValue for #name { #[inline] fn to_value(&self) -> #crate_ident::value::Value { let mut value = #crate_ident::value::Value::for_value_type::(); unsafe { #crate_ident::gobject_ffi::g_value_set_flags( #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, #crate_ident::translate::IntoGlib::into_glib(*self) ) } value } #[inline] fn value_type(&self) -> #crate_ident::Type { ::static_type() } } impl #crate_ident::HasParamSpec for #name { type ParamSpec = #crate_ident::ParamSpecFlags; type SetValue = Self; type BuilderFn = fn(&::core::primitive::str) -> #crate_ident::ParamSpecFlagsBuilder; fn param_spec_builder() -> Self::BuilderFn { |name| Self::ParamSpec::builder(name) } } impl ::std::convert::From<#name> for #crate_ident::Value { #[inline] fn from(v: #name) -> Self { #crate_ident::value::ToValue::to_value(&v) } } impl #crate_ident::prelude::StaticType for #name { #[inline] fn static_type() -> #crate_ident::Type { Self::register_flags() } } #register_flags } } // Registers the flags as a static type. fn register_flags_as_static( crate_ident: &TokenStream, name: &syn::Ident, gtype_name: syn::LitStr, allow_name_conflict: bool, g_flags_values: TokenStream, nb_flags_values: usize, ) -> TokenStream { let type_name_snippet = if allow_name_conflict { quote! { unsafe { let mut i = 0; loop { let type_name = ::std::ffi::CString::new(if i == 0 { #gtype_name } else { format!("{}-{}", #gtype_name, i) }) .unwrap(); if #crate_ident::gobject_ffi::g_type_from_name(type_name.as_ptr()) == #crate_ident::gobject_ffi::G_TYPE_INVALID { break type_name; } i += 1; } } } } else { quote! { unsafe { let type_name = ::std::ffi::CString::new(#gtype_name).unwrap(); assert_eq!( #crate_ident::gobject_ffi::g_type_from_name(type_name.as_ptr()), #crate_ident::gobject_ffi::G_TYPE_INVALID, "Type {} has already been registered", type_name.to_str().unwrap() ); type_name } } }; // registers the flags on first use (lazy registration). quote! { impl #name { /// Registers the flags only once. #[inline] fn register_flags() -> #crate_ident::Type { static TYPE: ::std::sync::OnceLock<#crate_ident::Type> = ::std::sync::OnceLock::new(); *TYPE.get_or_init(|| { static mut VALUES: [#crate_ident::gobject_ffi::GFlagsValue; #nb_flags_values] = [ #g_flags_values #crate_ident::gobject_ffi::GFlagsValue { value: 0, value_name: ::std::ptr::null(), value_nick: ::std::ptr::null(), }, ]; let type_name = #type_name_snippet; unsafe { let type_ = #crate_ident::gobject_ffi::g_flags_register_static(type_name.as_ptr(), VALUES.as_ptr()); let type_: #crate_ident::Type = #crate_ident::translate::from_glib(type_); assert!(type_.is_valid()); type_ } }) } } } } // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). // Flags can be reregistered as a dynamic type. fn register_flags_as_dynamic( crate_ident: &TokenStream, plugin_ty: TokenStream, lazy_registration: bool, name: &syn::Ident, gtype_name: syn::LitStr, g_flags_values: TokenStream, nb_flags_values: usize, ) -> TokenStream { // Wrap each GFlagsValue to FlagsValue let g_flags_values_expr: syn::ExprArray = syn::parse_quote! { [#g_flags_values] }; let flags_values_iter = g_flags_values_expr.elems.iter().map(|v| { quote_spanned! {syn::spanned::Spanned::span(&v)=> #crate_ident::FlagsValue::unsafe_from(#v), } }); let flags_values = quote! { #crate_ident::enums::FlagsValuesStorage<#nb_flags_values> = unsafe { #crate_ident::enums::FlagsValuesStorage::<#nb_flags_values>::new([ #(#flags_values_iter)* ]) } }; // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). // Flags can be reregistered as a dynamic type. if lazy_registration { // registers the flags as a dynamic type on the first use (lazy registration). // a weak reference on the plugin is stored and will be used later on the first use of the flags. // this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the flags have been registered. // the registration status type. let registration_status_type = format_ident!("{}RegistrationStatus", name); // name of the static variable to store the registration status. let registration_status = format_ident!( "{}", registration_status_type.to_string().to_shouty_snake_case() ); // name of the static array to store the flags values. let flags_values_array = format_ident!("{}_VALUES", name.to_string().to_shouty_snake_case()); quote! { /// The registration status type: a tuple of the weak reference on the plugin and of the GLib type. struct #registration_status_type(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type); unsafe impl Send for #registration_status_type {} /// The registration status protected by a mutex guarantees so that no other threads are concurrently accessing the data. static #registration_status: ::std::sync::Mutex> = ::std::sync::Mutex::new(None); /// Array of `FlagsValue` for the possible flags values. static #flags_values_array: #flags_values; impl #name { /// Registers the flags as a dynamic type within the plugin only once. /// Plugin must have been used at least once. /// Do nothing if plugin has never been used or if the flags are already registered as a dynamic type. #[inline] fn register_flags() -> #crate_ident::Type { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so the flags cannot be registered as a dynamic type. None => #crate_ident::Type::INVALID, // plugin has been used and the flags have not been registered yet, so registers tem as a dynamic type. Some(#registration_status_type(type_plugin, type_)) if !type_.is_valid() => { *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_flags(type_plugin.upgrade().unwrap().as_ref(), #gtype_name, #flags_values_array.as_ref()); *type_ }, // plugin has been used and the flags have already been registered as a dynamic type. Some(#registration_status_type(_, type_)) => *type_ } } /// Depending on the plugin lifecycle state and on the registration status of the flags: /// If plugin is used (and has loaded the implementation) for the first time, postpones the registration and stores a weak reference on the plugin. /// If plugin is reused (and has reloaded the implementation) and the flags have been already registered as a dynamic type, reregisters them. /// Flags can be reregistered several times as a dynamic type. /// If plugin is reused (and has reloaded the implementation) and the flags have not been registered yet as a dynamic type, do nothing. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used (this is the first time), so postpones registration of the flags as a dynamic type on the first use. None => { *registration_status = Some(#registration_status_type(#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); true }, // plugin has been used at least one time and the flags have been registered as a dynamic type at least one time, so re-registers them. Some(#registration_status_type(_, type_)) if type_.is_valid() => { *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_flags(type_plugin, #gtype_name, #flags_values_array.as_ref()); type_.is_valid() }, // plugin has been used at least one time but the flags have not been registered yet as a dynamic type, so keeps postponed registration. Some(_) => { true } } } /// Depending on the plugin lifecycle state and on the registration status of the flags: /// If plugin has been used (or reused) but the flags have not been registered yet as a dynamic type, cancels the postponed registration by deleting the weak reference on the plugin. /// Else do nothing. #[inline] pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so unload implementation is unexpected. None => false, // plugin has been used at least one time and the flags have been registered as a dynamic type at least one time. Some(#registration_status_type(_, type_)) if type_.is_valid() => true, // plugin has been used at least one time but the flags have not been registered yet as a dynamic type, so cancels the postponed registration. Some(_) => { *registration_status = None; true } } } } } } else { // registers immediately the flags as a dynamic type. // name of the static variable to store the GLib type. let gtype_status = format_ident!("{}_G_TYPE", name.to_string().to_shouty_snake_case()); quote! { /// The GLib type which can be safely shared between threads. static #gtype_status: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); impl #name { /// Do nothing as the flags has been registered on implementation load. #[inline] fn register_flags() -> #crate_ident::Type { let gtype = #gtype_status.load(::std::sync::atomic::Ordering::Acquire); unsafe { <#crate_ident::Type as #crate_ident::translate::FromGlib<#crate_ident::ffi::GType>>::from_glib(gtype) } } /// Registers the flags as a dynamic type within the plugin. /// The flags can be registered several times as a dynamic type. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { static VALUES: #flags_values; let gtype = #crate_ident::translate::IntoGlib::into_glib(<#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_flags(type_plugin, #gtype_name, VALUES.as_ref())); #gtype_status.store(gtype, ::std::sync::atomic::Ordering::Release); gtype != #crate_ident::gobject_ffi::G_TYPE_INVALID } /// Do nothing as flags registered as dynamic types are never unregistered. #[inline] pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { true } } } } } glib-macros-0.20.4/src/lib.rs000064400000000000000000001546331046102023000140250ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. mod boxed_derive; mod clone; mod clone_old; mod closure; mod closure_old; mod derived_properties_attribute; mod downgrade_derive; mod enum_derive; mod error_domain_derive; mod flags_attribute; mod object_impl_attributes; mod properties; mod shared_boxed_derive; mod value_delegate_derive; mod variant_derive; mod utils; use flags_attribute::AttrInput; use proc_macro::{TokenStream, TokenTree}; use proc_macro2::Span; use syn::{parse_macro_input, DeriveInput}; use utils::{parse_nested_meta_items_from_stream, NestedMetaItem}; /// Macro for passing variables as strong or weak references into a closure. /// /// This macro can be useful in combination with closures, e.g. signal handlers, to reduce the /// boilerplate required for passing strong or weak references into the closure. It will /// automatically create the new reference and pass it with the same name into the closure. /// /// If upgrading the weak reference to a strong reference inside the closure is failing, the /// closure is immediately returning an optional default return value. If none is provided, `()` is /// returned. /// /// **⚠️ IMPORTANT ⚠️** /// /// `glib` needs to be in scope, so unless it's one of the direct crate dependencies, you need to /// import it because `clone!` is using it. For example: /// /// ```rust,ignore /// use gtk::glib; /// ``` /// /// ### Debugging /// /// In case something goes wrong inside the `clone!` macro, we use the [`g_debug`] macro. Meaning /// that if you want to see these debug messages, you'll have to set the `G_MESSAGES_DEBUG` /// environment variable when running your code (either in the code directly or when running the /// binary) to either "all" or [`CLONE_MACRO_LOG_DOMAIN`]: /// /// [`g_debug`]: ../glib/macro.g_debug.html /// [`CLONE_MACRO_LOG_DOMAIN`]: ../glib/constant.CLONE_MACRO_LOG_DOMAIN.html /// /// ```rust,ignore /// use glib::CLONE_MACRO_LOG_DOMAIN; /// /// std::env::set_var("G_MESSAGES_DEBUG", CLONE_MACRO_LOG_DOMAIN); /// std::env::set_var("G_MESSAGES_DEBUG", "all"); /// ``` /// /// Or: /// /// ```bash /// $ G_MESSAGES_DEBUG=all ./binary /// ``` /// /// ### Passing a strong reference /// /// ``` /// use glib; /// use glib_macros::clone; /// use std::rc::Rc; /// /// let v = Rc::new(1); /// let closure = clone!( /// #[strong] v, /// move |x| { /// println!("v: {}, x: {}", v, x); /// }, /// ); /// /// closure(2); /// ``` /// /// ### Passing a weak reference /// /// ``` /// use glib; /// use glib_macros::clone; /// use std::rc::Rc; /// /// let u = Rc::new(2); /// let closure = clone!( /// #[weak] /// u, /// move |x| { /// println!("u: {}, x: {}", u, x); /// }, /// ); /// /// closure(3); /// ``` /// /// #### Allowing a nullable weak reference /// /// In some cases, even if the weak references can't be retrieved, you might want to still have /// your closure called. In this case, you need to use `#[weak_allow_none]` instead of `#[weak]`: /// /// ``` /// use glib; /// use glib_macros::clone; /// use std::rc::Rc; /// /// let closure = { /// // This `Rc` won't be available in the closure because it's dropped at the end of the /// // current block /// let u = Rc::new(2); /// clone!( /// #[weak_allow_none] /// u, /// move |x| { /// // We need to use a Debug print for `u` because it'll be an `Option`. /// println!("u: {:?}, x: {}", u, x); /// true /// }, /// ) /// }; /// /// assert_eq!(closure(3), true); /// ``` /// /// ### Creating owned values from references (`ToOwned`) /// /// ``` /// use glib; /// use glib_macros::clone; /// /// let v = "123"; /// let closure = clone!( /// #[to_owned] v, /// move |x| { /// // v is passed as `String` here /// println!("v: {}, x: {}", v, x); /// }, /// ); /// /// closure(2); /// ``` /// /// ### Renaming variables /// /// ``` /// use glib; /// use glib_macros::clone; /// use std::rc::Rc; /// /// let v = Rc::new(1); /// let u = Rc::new(2); /// let closure = clone!( /// #[strong(rename_to = y)] /// v, /// #[weak] u, /// move |x| { /// println!("v as y: {}, u: {}, x: {}", y, u, x); /// }, /// ); /// /// closure(3); /// ``` /// /// ### Providing a return value if upgrading a weak reference fails /// /// By default, `()` is returned if upgrading a weak reference fails. This behaviour can be /// adjusted in two different ways: /// /// Either by providing the value yourself using one of /// /// * `#[upgrade_or]`: Requires an expression that returns a `Copy` value of the expected return type, /// * `#[upgrade_or_else]`: Requires a closure that returns a value of the expected return type, /// * `#[upgrade_or_default]`: Requires that the return type implements `Default` and returns that. /// /// ``` /// use glib; /// use glib_macros::clone; /// use std::rc::Rc; /// /// let v = Rc::new(1); /// let closure = clone!( /// #[weak] v, /// #[upgrade_or] /// false, /// move |x| { /// println!("v: {}, x: {}", v, x); /// true /// }, /// ); /// /// // Drop value so that the weak reference can't be upgraded. /// drop(v); /// /// assert_eq!(closure(2), false); /// ``` /// /// Or by using `#[upgrade_or_panic]`: If the value fails to get upgraded, it'll panic. /// /// ```should_panic /// # use glib; /// # use glib_macros::clone; /// # use std::rc::Rc; /// # let v = Rc::new(1); /// let closure = clone!( /// #[weak] v, /// #[upgrade_or_panic] /// move |x| { /// println!("v: {}, x: {}", v, x); /// true /// }, /// ); /// # drop(v); /// # assert_eq!(closure(2), false); /// ``` /// /// ### Errors /// /// Here is a list of errors you might encounter: /// /// **Missing `#[weak]` or `#[strong]`**: /// /// ```compile_fail /// # use glib; /// # use glib_macros::clone; /// # use std::rc::Rc; /// let v = Rc::new(1); /// /// let closure = clone!( /// v, /// move |x| println!("v: {}, x: {}", v, x), /// ); /// # drop(v); /// # closure(2); /// ``` /// /// **Passing `self` as an argument**: /// /// ```compile_fail /// # use glib; /// # use glib_macros::clone; /// # use std::rc::Rc; /// #[derive(Debug)] /// struct Foo; /// /// impl Foo { /// fn foo(&self) { /// let closure = clone!( /// #[strong] self, /// move |x| { /// println!("self: {:?}", self); /// }, /// ); /// # closure(2); /// } /// } /// ``` /// /// If you want to use `self` directly, you'll need to rename it: /// /// ``` /// # use glib; /// # use glib_macros::clone; /// # use std::rc::Rc; /// #[derive(Debug)] /// struct Foo; /// /// impl Foo { /// fn foo(&self) { /// let closure = clone!( /// #[strong(rename_to = this)] /// self, /// move |x| { /// println!("self: {:?}", this); /// }, /// ); /// # closure(2); /// } /// } /// ``` /// /// **Passing fields directly** /// /// ```compile_fail /// # use glib; /// # use glib_macros::clone; /// # use std::rc::Rc; /// #[derive(Debug)] /// struct Foo { /// v: Rc, /// } /// /// impl Foo { /// fn foo(&self) { /// let closure = clone!( /// #[strong] self.v, /// move |x| { /// println!("self.v: {:?}", v); /// }, /// ); /// # closure(2); /// } /// } /// ``` /// /// You can do it by renaming it: /// /// ``` /// # use glib; /// # use glib_macros::clone; /// # use std::rc::Rc; /// # struct Foo { /// # v: Rc, /// # } /// impl Foo { /// fn foo(&self) { /// let closure = clone!( /// #[strong(rename_to = v)] /// self.v, /// move |x| { /// println!("self.v: {}", v); /// }, /// ); /// # closure(2); /// } /// } /// ``` #[proc_macro] pub fn clone(item: TokenStream) -> TokenStream { // Check if this is an old-style clone macro invocation. // These always start with an '@' punctuation. let Some(first) = item.clone().into_iter().next() else { return syn::Error::new(Span::call_site(), "expected a closure or async block") .to_compile_error() .into(); }; match first { TokenTree::Punct(ref p) if p.to_string() == "@" => clone_old::clone_inner(item), _ => clone::clone_inner(item), } } /// Macro for creating a [`Closure`] object. This is a wrapper around [`Closure::new`] that /// automatically type checks its arguments at run-time. /// /// A `Closure` takes [`Value`] objects as inputs and output. This macro will automatically convert /// the inputs to Rust types when invoking its callback, and then will convert the output back to a /// `Value`. All inputs must implement the [`FromValue`] trait, and outputs must either implement /// the [`ToValue`] trait or be the unit type `()`. Type-checking of inputs is done at run-time; if /// incorrect types are passed via [`Closure::invoke`] then the closure will panic. Note that when /// passing input types derived from [`Object`] or [`Interface`], you must take care to upcast to /// the exact object or interface type that is being received. /// /// Similarly to [`clone!`](crate::clone!), this macro can be useful in combination with signal /// handlers to reduce boilerplate when passing references. Unique to `Closure` objects is the /// ability to watch an object using the `#[watch]` attribute. Only an [`Object`] value can be /// passed to `#[watch]`, and only one object can be watched per closure. When an object is watched, /// a weak reference to the object is held in the closure. When the object is destroyed, the /// closure will become invalidated: all signal handlers connected to the closure will become /// disconnected, and any calls to [`Closure::invoke`] on the closure will be silently ignored. /// Internally, this is accomplished using [`Object::watch_closure`] on the watched object. /// /// The `#[weak]`, `#[weak_allow_none]`, `#[strong]`, `#[to_owned]` captures are also supported and /// behave the same as in [`clone!`](crate::clone!), as is aliasing captures via `rename_to`. /// Similarly, upgrade failure of weak references can be adjusted via `#[upgrade_or]`, /// `#[upgrade_or_else]`, `#[upgrade_or_default]` and `#[upgrade_or_panic]`. /// /// Notably, these captures are able to reference `Rc` and `Arc` values in addition to `Object` /// values. /// /// [`Closure`]: ../glib/closure/struct.Closure.html /// [`Closure::new`]: ../glib/closure/struct.Closure.html#method.new /// [`Closure::new_local`]: ../glib/closure/struct.Closure.html#method.new_local /// [`Closure::invoke`]: ../glib/closure/struct.Closure.html#method.invoke /// [`Value`]: ../glib/value/struct.Value.html /// [`FromValue`]: ../glib/value/trait.FromValue.html /// [`ToValue`]: ../glib/value/trait.ToValue.html /// [`Interface`]: ../glib/object/struct.Interface.html /// [`Object`]: ../glib/object/struct.Object.html /// [`Object::watch_closure`]: ../glib/object/trait.ObjectExt.html#tymethod.watch_closure /// **⚠️ IMPORTANT ⚠️** /// /// `glib` needs to be in scope, so unless it's one of the direct crate dependencies, you need to /// import it because `closure!` is using it. For example: /// /// ```rust,ignore /// use gtk::glib; /// ``` /// /// ### Using as a closure object /// /// ``` /// use glib_macros::closure; /// /// let concat_str = closure!(|s: &str| s.to_owned() + " World"); /// let result = concat_str.invoke::(&[&"Hello"]); /// assert_eq!(result, "Hello World"); /// ``` /// /// ### Connecting to a signal /// /// For wrapping closures that can't be sent across threads, the /// [`closure_local!`](crate::closure_local!) macro can be used. It has the same syntax as /// `closure!`, but instead uses [`Closure::new_local`] internally. /// /// ``` /// use glib; /// use glib::prelude::*; /// use glib_macros::closure_local; /// /// let obj = glib::Object::new::(); /// obj.connect_closure( /// "notify", false, /// closure_local!(|_obj: glib::Object, pspec: glib::ParamSpec| { /// println!("property notify: {}", pspec.name()); /// })); /// ``` /// /// ### Object Watching /// /// ``` /// use glib; /// use glib::prelude::*; /// use glib_macros::closure_local; /// /// let closure = { /// let obj = glib::Object::new::(); /// let closure = closure_local!( /// #[watch] obj, /// move || { /// obj.type_().name() /// }, /// ); /// assert_eq!(closure.invoke::(&[]), "GObject"); /// closure /// }; /// // `obj` is dropped, closure invalidated so it always does nothing and returns None /// closure.invoke::<()>(&[]); /// ``` /// /// `#[watch]` has special behavior when connected to a signal: /// /// ``` /// use glib; /// use glib::prelude::*; /// use glib_macros::closure_local; /// /// let obj = glib::Object::new::(); /// { /// let other = glib::Object::new::(); /// obj.connect_closure( /// "notify", false, /// closure_local!( /// #[watch(rename_to = b)] /// other, /// move |a: glib::Object, pspec: glib::ParamSpec| { /// let value = a.property_value(pspec.name()); /// b.set_property(pspec.name(), &value); /// }, /// ), /// ); /// // The signal handler will disconnect automatically at the end of this /// // block when `other` is dropped. /// } /// ``` /// /// ### Weak and Strong References /// /// ``` /// use glib; /// use glib::prelude::*; /// use glib_macros::closure; /// use std::sync::Arc; /// /// let closure = { /// let a = Arc::new(String::from("Hello")); /// let b = Arc::new(String::from("World")); /// let c = "!"; /// let closure = closure!( /// #[strong] a, /// #[weak_allow_none] /// b, /// #[to_owned] /// c, /// move || { /// // `a` is Arc, `b` is Option>, `c` is a `String` /// format!("{} {}{}", a, b.as_ref().map(|b| b.as_str()).unwrap_or_else(|| "Moon"), c) /// }, /// ); /// assert_eq!(closure.invoke::(&[]), "Hello World!"); /// closure /// }; /// // `a`, `c` still kept alive, `b` is dropped /// assert_eq!(closure.invoke::(&[]), "Hello Moon!"); /// ``` #[proc_macro] pub fn closure(item: TokenStream) -> TokenStream { // Check if this is an old-style closure macro invocation. // These always start with an '@' punctuation. let Some(first) = item.clone().into_iter().next() else { return syn::Error::new(Span::call_site(), "expected a closure") .to_compile_error() .into(); }; match first { TokenTree::Punct(ref p) if p.to_string() == "@" => closure_old::closure_inner(item, "new"), _ => closure::closure_inner(item, "new"), } } /// The same as [`closure!`](crate::closure!) but uses [`Closure::new_local`] as a constructor. /// This is useful for closures which can't be sent across threads. See the documentation of /// [`closure!`](crate::closure!) for details. /// /// [`Closure::new_local`]: ../glib/closure/struct.Closure.html#method.new_local #[proc_macro] pub fn closure_local(item: TokenStream) -> TokenStream { // Check if this is an old-style closure macro invocation. // These always start with an '@' punctuation. let Some(first) = item.clone().into_iter().next() else { return syn::Error::new(Span::call_site(), "expected a closure") .to_compile_error() .into(); }; match first { TokenTree::Punct(ref p) if p.to_string() == "@" => { closure_old::closure_inner(item, "new_local") } _ => closure::closure_inner(item, "new_local"), } } /// Derive macro to register a Rust enum in the GLib type system and derive the /// [`glib::Value`] traits. /// /// # Example /// /// ``` /// use glib::prelude::*; /// use glib::subclass::prelude::*; /// /// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)] /// #[enum_type(name = "MyEnum")] /// enum MyEnum { /// Val, /// #[enum_value(name = "My Val")] /// ValWithCustomName, /// #[enum_value(name = "My Other Val", nick = "other")] /// ValWithCustomNameAndNick, /// } /// ``` /// /// An enum can be registered as a dynamic type by setting the derive macro /// helper attribute `enum_dynamic`: /// /// ```ignore /// use glib::prelude::*; /// use glib::subclass::prelude::*; /// /// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)] /// #[enum_type(name = "MyEnum")] /// #[enum_dynamic] /// enum MyEnum { /// ... /// } /// ``` /// /// As a dynamic type, an enum must be explicitly registered when the system /// loads the implementation (see [`TypePlugin`] and [`TypeModule`]). /// Therefore, whereas an enum can be registered only once as a static type, /// it can be registered several times as a dynamic type. /// /// An enum registered as a dynamic type is never unregistered. The system /// calls [`TypePluginExt::unuse`] to unload the implementation. If the /// [`TypePlugin`] subclass is a [`TypeModule`], the enum registered as a /// dynamic type is marked as unloaded and must be registered again when the /// module is reloaded. /// /// The derive macro helper attribute `enum_dynamic` provides two behaviors /// when registering an enum as a dynamic type: /// /// - lazy registration: by default an enum is registered as a dynamic type /// when the system loads the implementation (e.g. when the module is loaded). /// Optionally setting `lazy_registration` to `true` postpones registration on /// the first use (when `static_type()` is called for the first time): /// /// ```ignore /// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)] /// #[enum_type(name = "MyEnum")] /// #[enum_dynamic(lazy_registration = true)] /// enum MyEnum { /// ... /// } /// ``` /// /// - registration within [`TypeModule`] subclass or within [`TypePlugin`] /// subclass: an enum is usually registered as a dynamic type within a /// [`TypeModule`] subclass: /// /// ```ignore /// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)] /// #[enum_type(name = "MyModuleEnum")] /// #[enum_dynamic] /// enum MyModuleEnum { /// ... /// } /// ... /// #[derive(Default)] /// pub struct MyModule; /// ... /// impl TypeModuleImpl for MyModule { /// fn load(&self) -> bool { /// // registers enums as dynamic types. /// let my_module = self.obj(); /// let type_module: &glib::TypeModule = my_module.upcast_ref(); /// MyModuleEnum::on_implementation_load(type_module) /// } /// ... /// } /// ``` /// /// Optionally setting `plugin_type` allows to register an enum as a dynamic /// type within a [`TypePlugin`] subclass that is not a [`TypeModule`]: /// /// ```ignore /// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum)] /// #[enum_type(name = "MyPluginEnum")] /// #[enum_dynamic(plugin_type = MyPlugin)] /// enum MyPluginEnum { /// ... /// } /// ... /// #[derive(Default)] /// pub struct MyPlugin; /// ... /// impl TypePluginImpl for MyPlugin { /// fn use_plugin(&self) { /// // register enums as dynamic types. /// let my_plugin = self.obj(); /// MyPluginEnum::on_implementation_load(my_plugin.as_ref()); /// } /// ... /// } /// ``` /// /// [`glib::Value`]: ../glib/value/struct.Value.html /// [`TypePlugin`]: ../glib/gobject/type_plugin/struct.TypePlugin.html /// [`TypeModule`]: ../glib/gobject/type_module/struct.TypeModule.html /// [`TypePluginExt::unuse`]: ../glib/gobject/type_plugin/trait.TypePluginExt. #[proc_macro_derive(Enum, attributes(enum_type, enum_dynamic, enum_value))] pub fn enum_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); enum_derive::impl_enum(&input) .unwrap_or_else(syn::Error::into_compile_error) .into() } /// Attribute macro for defining flags using the `bitflags` crate. /// This macro will also define a `GFlags::type_` function and /// the [`glib::Value`] traits. /// /// The expected `GType` name has to be passed as macro attribute. /// The name and nick of each flag can also be optionally defined. /// Default name is the flag identifier in CamelCase and default nick /// is the identifier in kebab-case. /// Combined flags should not be registered with the `GType` system /// and so need to be tagged with the `#[flags_value(skip)]` attribute. /// /// # Example /// /// ``` /// use glib::prelude::*; /// use glib::subclass::prelude::*; /// /// #[glib::flags(name = "MyFlags")] /// enum MyFlags { /// #[flags_value(name = "Flag A", nick = "nick-a")] /// A = 0b00000001, /// #[flags_value(name = "Flag B")] /// B = 0b00000010, /// #[flags_value(skip)] /// AB = Self::A.bits() | Self::B.bits(), /// C = 0b00000100, /// } /// ``` /// /// The flags can be registered as a dynamic type by setting the macro helper /// attribute `flags_dynamic`: /// ```ignore /// use glib::prelude::*; /// use glib::subclass::prelude::*; /// /// #[glib::flags(name = "MyFlags")] /// #[flags_dynamic] /// enum MyFlags { /// ... /// } /// ``` /// /// As a dynamic type, the flags must be explicitly registered when the system /// loads the implementation (see [`TypePlugin`] and [`TypeModule`]). /// Therefore, whereas the flags can be registered only once as a static type, /// they can be registered several times as a dynamic type. /// /// The flags registered as a dynamic type are never unregistered. The system /// calls [`TypePluginExt::unuse`] to unload the implementation. If the /// [`TypePlugin`] subclass is a [`TypeModule`], the flags registered as a /// dynamic type are marked as unloaded and must be registered again when the /// module is reloaded. /// /// The macro helper attribute `flags_dynamic` provides two behaviors when /// registering the flags as a dynamic type: /// /// - lazy registration: by default the flags are registered as a dynamic type /// when the system loads the implementation (e.g. when the module is loaded). /// Optionally setting `lazy_registration` to `true` postpones registration on /// the first use (when `static_type()` is called for the first time): /// /// ```ignore /// #[glib::flags(name = "MyFlags")] /// #[flags_dynamic(lazy_registration = true)] /// enum MyFlags { /// ... /// } /// ``` /// /// - registration within [`TypeModule`] subclass or within [`TypePlugin`] /// subclass: the flags are usually registered as a dynamic type within a /// [`TypeModule`] subclass: /// /// ```ignore /// #[glib::flags(name = "MyModuleFlags")] /// #[flags_dynamic] /// enum MyModuleFlags { /// ... /// } /// ... /// #[derive(Default)] /// pub struct MyModule; /// ... /// impl TypeModuleImpl for MyModule { /// fn load(&self) -> bool { /// // registers flags as dynamic types. /// let my_module = self.obj(); /// let type_module: &glib::TypeModule = my_module.upcast_ref(); /// MyModuleFlags::on_implementation_load(type_module) /// } /// ... /// } /// ``` /// /// Optionally setting `plugin_type` allows to register the flags as a dynamic /// type within a [`TypePlugin`] subclass that is not a [`TypeModule`]: /// ```ignore /// #[glib::flags(name = "MyModuleFlags")] /// #[flags_dynamic(plugin_type = MyPlugin)] /// enum MyModuleFlags { /// ... /// } /// ... /// #[derive(Default)] /// pub struct MyPlugin; /// ... /// impl TypePluginImpl for MyPlugin { /// fn use_plugin(&self) { /// // register flags as dynamic types. /// let my_plugin = self.obj(); /// MyPluginFlags::on_implementation_load(my_plugin.as_ref()); /// } /// ... /// } /// ``` /// /// [`glib::Value`]: ../glib/value/struct.Value.html /// [`TypePlugin`]: ../glib/gobject/type_plugin/struct.TypePlugin.html /// [`TypeModule`]: ../glib/gobject/type_module/struct.TypeModule.html /// [`TypePluginExt::unuse`]: ../glib/gobject/type_plugin/trait.TypePluginExt. #[proc_macro_attribute] pub fn flags(attr: TokenStream, item: TokenStream) -> TokenStream { let mut name = NestedMetaItem::::new("name") .required() .value_required(); let mut allow_name_conflict_attr = NestedMetaItem::::new("allow_name_conflict").value_optional(); if let Err(e) = parse_nested_meta_items_from_stream( attr.into(), &mut [&mut name, &mut allow_name_conflict_attr], ) { return e.to_compile_error().into(); } let allow_name_conflict = allow_name_conflict_attr.found || allow_name_conflict_attr .value .map(|b| b.value()) .unwrap_or(false); let attr_meta = AttrInput { enum_name: name.value.unwrap(), allow_name_conflict, }; syn::parse::(item) .map_err(|_| syn::Error::new(Span::call_site(), flags_attribute::WRONG_PLACE_MSG)) .map(|mut input| flags_attribute::impl_flags(attr_meta, &mut input)) .unwrap_or_else(syn::Error::into_compile_error) .into() } /// Derive macro for defining a GLib error domain and its associated /// [`ErrorDomain`] trait. /// /// # Example /// /// ``` /// use glib::prelude::*; /// use glib::subclass::prelude::*; /// /// #[derive(Debug, Copy, Clone, glib::ErrorDomain)] /// #[error_domain(name = "ex-foo")] /// enum Foo { /// Blah, /// Baaz, /// } /// ``` /// /// [`ErrorDomain`]: ../glib/error/trait.ErrorDomain.html #[proc_macro_derive(ErrorDomain, attributes(error_domain))] pub fn error_domain_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); error_domain_derive::impl_error_domain(&input) .unwrap_or_else(syn::Error::into_compile_error) .into() } /// Derive macro for defining a [`BoxedType`]`::type_` function and /// the [`glib::Value`] traits. Optionally, the type can be marked as /// `nullable` to get an implementation of `glib::value::ToValueOptional`. /// /// # Example /// /// ``` /// use glib::prelude::*; /// use glib::subclass::prelude::*; /// /// #[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)] /// #[boxed_type(name = "MyBoxed")] /// struct MyBoxed(String); /// /// #[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)] /// #[boxed_type(name = "MyNullableBoxed", nullable)] /// struct MyNullableBoxed(String); /// ``` /// /// [`BoxedType`]: ../glib/subclass/boxed/trait.BoxedType.html /// [`glib::Value`]: ../glib/value/struct.Value.html #[proc_macro_derive(Boxed, attributes(boxed_type))] pub fn boxed_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); boxed_derive::impl_boxed(&input) .unwrap_or_else(syn::Error::into_compile_error) .into() } /// Derive macro for defining a [`SharedType`]`::get_type` function and /// the [`glib::Value`] traits. Optionally, the type can be marked as /// `nullable` to get an implementation of `glib::value::ToValueOptional`. /// /// # Example /// /// ``` /// use glib::prelude::*; /// use glib::subclass::prelude::*; /// /// #[derive(Clone, Debug, PartialEq, Eq)] /// struct MySharedInner { /// foo: String, /// } /// /// #[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)] /// #[shared_boxed_type(name = "MySharedBoxed")] /// struct MySharedBoxed(std::sync::Arc); /// /// #[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)] /// #[shared_boxed_type(name = "MyNullableSharedBoxed", nullable)] /// struct MyNullableSharedBoxed(std::sync::Arc); /// ``` /// /// [`SharedType`]: ../glib/subclass/shared/trait.SharedType.html /// [`glib::Value`]: ../glib/value/struct.Value.html #[proc_macro_derive(SharedBoxed, attributes(shared_boxed_type))] pub fn shared_boxed_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); shared_boxed_derive::impl_shared_boxed(&input) .unwrap_or_else(syn::Error::into_compile_error) .into() } /// Macro for boilerplate of [`ObjectSubclass`] implementations. /// /// This adds implementations for the `type_data()` and `type_()` methods, /// which should probably never be defined differently. /// /// It provides default values for the `Instance`, `Class`, and `Interfaces` /// type parameters. If these are present, the macro will use the provided value /// instead of the default. /// /// Usually the defaults for `Instance` and `Class` will work. `Interfaces` is /// necessary for types that implement interfaces. /// /// ```ignore /// type Instance = glib::subclass::basic::InstanceStruct; /// type Class = glib::subclass::basic::ClassStruct; /// type Interfaces = (); /// ``` /// /// If no `new()` or `with_class()` method is provided, the macro adds a `new()` /// implementation calling `Default::default()`. So the type needs to implement /// `Default`, or this should be overridden. /// /// ```ignore /// fn new() -> Self { /// Default::default() /// } /// ``` /// /// An object subclass can be registered as a dynamic type by setting the macro /// helper attribute `object_class_dynamic`: /// /// ```ignore /// #[derive(Default)] /// pub struct MyType; /// /// #[glib::object_subclass] /// #[object_subclass_dynamic] /// impl ObjectSubclass for MyType { ... } /// ``` /// /// As a dynamic type, an object subclass must be explicitly registered when /// the system loads the implementation (see [`TypePlugin`] and [`TypeModule`]). /// Therefore, whereas an object subclass can be registered only once as a /// static type, it can be registered several times as a dynamic type. /// /// An object subclass registered as a dynamic type is never unregistered. The /// system calls [`TypePluginExt::unuse`] to unload the implementation. If the /// [`TypePlugin`] subclass is a [`TypeModule`], the object subclass registered /// as a dynamic type is marked as unloaded and must be registered again when /// the module is reloaded. /// /// The macro helper attribute `object_class_dynamic` provides two behaviors /// when registering an object subclass as a dynamic type: /// /// - lazy registration: by default an object subclass is registered as a /// dynamic type when the system loads the implementation (e.g. when the module /// is loaded). Optionally setting `lazy_registration` to `true` postpones /// registration on the first use (when `static_type()` is called for the first /// time): /// /// ```ignore /// #[derive(Default)] /// pub struct MyType; /// /// #[glib::object_subclass] /// #[object_subclass_dynamic(lazy_registration = true)] /// impl ObjectSubclass for MyType { ... } /// ``` /// /// - registration within [`TypeModule`] subclass or within [`TypePlugin`] /// subclass: an object subclass is usually registered as a dynamic type within /// a [`TypeModule`] subclass: /// /// ```ignore /// #[derive(Default)] /// pub struct MyModuleType; /// /// #[glib::object_subclass] /// #[object_subclass_dynamic] /// impl ObjectSubclass for MyModuleType { ... } /// ... /// #[derive(Default)] /// pub struct MyModule; /// ... /// impl TypeModuleImpl for MyModule { /// fn load(&self) -> bool { /// // registers object subclasses as dynamic types. /// let my_module = self.obj(); /// let type_module: &glib::TypeModule = my_module.upcast_ref(); /// MyModuleType::on_implementation_load(type_module) /// } /// ... /// } /// ``` /// /// Optionally setting `plugin_type` allows to register an object subclass as a /// dynamic type within a [`TypePlugin`] subclass that is not a [`TypeModule`]: /// /// ```ignore /// #[derive(Default)] /// pub struct MyPluginType; /// /// #[glib::object_subclass] /// #[object_subclass_dynamic(plugin_type = MyPlugin)] /// impl ObjectSubclass for MyPluginType { ... } /// ... /// #[derive(Default)] /// pub struct MyPlugin; /// ... /// impl TypePluginImpl for MyPlugin { /// fn use_plugin(&self) { /// // register object subclasses as dynamic types. /// let my_plugin = self.obj(); /// MyPluginType::on_implementation_load(my_plugin.as_ref()); /// } /// ... /// } /// ``` /// /// [`ObjectSubclass`]: ../glib/subclass/types/trait.ObjectSubclass.html /// [`TypePlugin`]: ../glib/gobject/type_plugin/struct.TypePlugin.html /// [`TypeModule`]: ../glib/gobject/type_module/struct.TypeModule.html /// [`TypePluginExt::unuse`]: ../glib/gobject/type_plugin/trait.TypePluginExt.html#method.unuse #[proc_macro_attribute] pub fn object_subclass(_attr: TokenStream, item: TokenStream) -> TokenStream { 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. /// /// This adds implementations for the `get_type()` method, which should probably never be defined /// differently. /// /// It provides default values for the `Prerequisites` type parameter. If this is present, the macro /// will use the provided value instead of the default. /// /// `Prerequisites` are interfaces for types that require a specific base class or interfaces. /// /// ```ignore /// type Prerequisites = (); /// ``` /// /// An object interface can be registered as a dynamic type by setting the /// macro helper attribute `object_interface_dynamic`: /// ```ignore /// pub struct MyInterface { /// parent: glib::gobject_ffi::GTypeInterface, /// } /// #[glib::object_interface] /// #[object_interface_dynamic] /// unsafe impl ObjectInterface for MyInterface { ... } /// ``` /// /// As a dynamic type, an object interface must be explicitly registered when /// the system loads the implementation (see [`TypePlugin`] and [`TypeModule`]). /// Therefore, whereas an object interface can be registered only once as a /// static type, it can be registered several times as a dynamic type. /// /// An object interface registered as a dynamic type is never unregistered. The /// system calls [`TypePluginExt::unuse`] to unload the implementation. If the /// [`TypePlugin`] subclass is a [`TypeModule`], the object interface /// registered as a dynamic type is marked as unloaded and must be registered /// again when the module is reloaded. /// /// The macro helper attribute `object_interface_dynamic` provides two /// behaviors when registering an object interface as a dynamic type: /// /// - lazy registration: by default an object interface is registered as a /// dynamic type when the system loads the implementation (e.g. when the module /// is loaded). Optionally setting `lazy_registration` to `true` postpones /// registration on the first use (when `type_()` is called for the first time): /// /// ```ignore /// pub struct MyInterface { /// parent: glib::gobject_ffi::GTypeInterface, /// } /// #[glib::object_interface] /// #[object_interface_dynamic(lazy_registration = true)] /// unsafe impl ObjectInterface for MyInterface { ... } /// ``` /// /// - registration within [`TypeModule`] subclass or within [`TypePlugin`] /// subclass: an object interface is usually registered as a dynamic type /// within a [`TypeModule`] subclass: /// /// ```ignore /// pub struct MyModuleInterface { /// parent: glib::gobject_ffi::GTypeInterface, /// } /// #[glib::object_interface] /// #[object_interface_dynamic] /// unsafe impl ObjectInterface for MyModuleInterface { ... } /// ... /// #[derive(Default)] /// pub struct MyModule; /// ... /// impl TypeModuleImpl for MyModule { /// fn load(&self) -> bool { /// // registers object interfaces as dynamic types. /// let my_module = self.obj(); /// let type_module: &glib::TypeModule = my_module.upcast_ref(); /// MyModuleInterface::on_implementation_load(type_module) /// } /// ... /// } /// ``` /// /// Optionally setting `plugin_type` allows to register an object interface as /// a dynamic type within a [`TypePlugin`] subclass that is not a [`TypeModule`]: /// /// ```ignore /// pub struct MyPluginInterface { /// parent: glib::gobject_ffi::GTypeInterface, /// } /// #[glib::object_interface] /// #[object_interface_dynamic(plugin_type = MyPlugin)] /// unsafe impl ObjectInterface for MyPluginInterface { ... } /// ... /// #[derive(Default)] /// pub struct MyPlugin; /// ... /// impl TypePluginImpl for MyPlugin { /// fn use_plugin(&self) { /// // register object interfaces as dynamic types. /// let my_plugin = self.obj(); /// MyPluginInterface::on_implementation_load(my_plugin.as_ref()); /// } /// ... /// } /// ``` /// /// [`ObjectInterface`]: ../glib/subclass/interface/trait.ObjectInterface.html /// [`TypePlugin`]: ../glib/gobject/type_plugin/struct.TypePlugin.html /// [`TypeModule`]: ../glib/gobject/type_module/struct.TypeModule.html /// [`TypePluginExt::unuse`]: ../glib/gobject/type_plugin/trait.TypePluginExt.html#method.unuse/// #[proc_macro_attribute] pub fn object_interface(_attr: TokenStream, item: TokenStream) -> TokenStream { 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 /// [`glib::clone::Upgrade`] traits and a weak type. /// /// # Examples /// /// ## New Type Idiom /// /// ```rust,ignore /// #[derive(glib::Downgrade)] /// pub struct FancyLabel(gtk::Label); /// /// impl FancyLabel { /// pub fn new(label: &str) -> Self { /// Self(gtk::LabelBuilder::new().label(label).build()) /// } /// /// pub fn flip(&self) { /// self.0.set_angle(180.0 - self.0.angle()); /// } /// } /// /// let fancy_label = FancyLabel::new("Look at me!"); /// let button = gtk::ButtonBuilder::new().label("Click me!").build(); /// button.connect_clicked( /// clone!( /// #[weak] /// fancy_label, /// move || fancy_label.flip(), /// ), /// ); /// ``` /// /// ## Generic New Type /// /// ```rust,ignore /// #[derive(glib::Downgrade)] /// pub struct TypedEntry(gtk::Entry, std::marker::PhantomData); /// /// impl for TypedEntry { /// // ... /// } /// ``` /// /// ## Structures and Enums /// /// ```rust,ignore /// #[derive(Clone, glib::Downgrade)] /// pub struct ControlButtons { /// pub up: gtk::Button, /// pub down: gtk::Button, /// pub left: gtk::Button, /// pub right: gtk::Button, /// } /// /// #[derive(Clone, glib::Downgrade)] /// pub enum DirectionButton { /// Left(gtk::Button), /// Right(gtk::Button), /// Up(gtk::Button), /// Down(gtk::Button), /// } /// ``` /// /// [`glib::clone::Downgrade`]: ../glib/clone/trait.Downgrade.html /// [`glib::clone::Upgrade`]: ../glib/clone/trait.Upgrade.html #[proc_macro_derive(Downgrade)] pub fn downgrade(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); downgrade_derive::impl_downgrade(input) } /// Derive macro for serializing/deserializing custom structs/enums as [`glib::Variant`]s. /// /// # Example /// /// ``` /// use glib::prelude::*; /// /// #[derive(Debug, PartialEq, Eq, glib::Variant)] /// struct Foo { /// some_string: String, /// some_int: i32, /// } /// /// let v = Foo { some_string: String::from("bar"), some_int: 1 }; /// let var = v.to_variant(); /// assert_eq!(var.get::(), Some(v)); /// ``` /// /// When storing `Vec`s of fixed size types it is a good idea to wrap these in /// `glib::FixedSizeVariantArray` as serialization/deserialization will be more efficient. /// /// # Example /// /// ``` /// use glib::prelude::*; /// /// #[derive(Debug, PartialEq, Eq, glib::Variant)] /// struct Foo { /// some_vec: glib::FixedSizeVariantArray, u32>, /// some_int: i32, /// } /// /// let v = Foo { some_vec: vec![1u32, 2u32].into(), some_int: 1 }; /// let var = v.to_variant(); /// assert_eq!(var.get::(), Some(v)); /// ``` /// /// Enums are serialized as a tuple `(sv)` with the first value as a [kebab case] string for the /// enum variant, or just `s` if this is a C-style enum. Some additional attributes are supported /// for enums: /// - `#[variant_enum(repr)]` to serialize the enum variant as an integer type instead of `s`. The /// `#[repr]` attribute must also be specified on the enum with a sized integer type, and the type /// must implement `Copy`. /// - `#[variant_enum(enum)]` uses [`EnumClass`] to serialize/deserialize as nicks. Meant for use /// with [`glib::Enum`](Enum). /// - `#[variant_enum(flags)]` uses [`FlagsClass`] to serialize/deserialize as nicks. Meant for use /// with [`glib::flags`](macro@flags). /// - `#[variant_enum(enum, repr)]` serializes as `i32`. Meant for use with [`glib::Enum`](Enum). /// The type must also implement `Copy`. /// - `#[variant_enum(flags, repr)]` serializes as `u32`. Meant for use with /// [`glib::flags`](macro@flags). /// /// # Example /// /// ``` /// use glib::prelude::*; /// /// #[derive(Debug, PartialEq, Eq, glib::Variant)] /// enum Foo { /// MyA, /// MyB(i32), /// MyC { some_int: u32, some_string: String } /// } /// /// let v = Foo::MyC { some_int: 1, some_string: String::from("bar") }; /// let var = v.to_variant(); /// assert_eq!(var.child_value(0).str(), Some("my-c")); /// assert_eq!(var.get::(), Some(v)); /// /// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Variant)] /// #[variant_enum(repr)] /// #[repr(u8)] /// enum Bar { /// A, /// B = 3, /// C = 7 /// } /// /// let v = Bar::B; /// let var = v.to_variant(); /// assert_eq!(var.get::(), Some(3)); /// assert_eq!(var.get::(), Some(v)); /// /// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::Enum, glib::Variant)] /// #[variant_enum(enum)] /// #[enum_type(name = "MyEnum")] /// enum MyEnum { /// Val, /// #[enum_value(name = "My Val")] /// ValWithCustomName, /// #[enum_value(name = "My Other Val", nick = "other")] /// ValWithCustomNameAndNick, /// } /// /// let v = MyEnum::ValWithCustomNameAndNick; /// let var = v.to_variant(); /// assert_eq!(var.str(), Some("other")); /// assert_eq!(var.get::(), Some(v)); /// ``` /// /// [`glib::Variant`]: ../glib/variant/struct.Variant.html /// [`EnumClass`]: ../glib/struct.EnumClass.html /// [`FlagsClass`]: ../glib/struct.FlagsClass.html /// [kebab case]: https://docs.rs/heck/0.4.0/heck/trait.ToKebabCase.html #[proc_macro_derive(Variant, attributes(variant_enum))] pub fn variant_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); variant_derive::impl_variant(input) .unwrap_or_else(syn::Error::into_compile_error) .into() } #[proc_macro] pub fn cstr_bytes(item: TokenStream) -> TokenStream { syn::parse::Parser::parse2( |stream: syn::parse::ParseStream<'_>| { let literal = stream.parse::()?; stream.parse::()?; let bytes = std::ffi::CString::new(literal.value()) .map_err(|e| syn::Error::new_spanned(&literal, format!("{e}")))? .into_bytes_with_nul(); let bytes = proc_macro2::Literal::byte_string(&bytes); Ok(quote::quote! { #bytes }.into()) }, item.into(), ) .unwrap_or_else(|e| e.into_compile_error().into()) } /// This macro enables you to derive object properties in a quick way. /// /// # Supported `#[property]` attributes /// | Attribute | Description | Default | Example | /// | --- | --- | --- | --- | /// | `name = "literal"` | The name of the property | field ident where `_` (leading and trailing `_` are trimmed) is replaced into `-` | `#[property(name = "prop-name")]` | /// | `type = expr` | The type of the property | inferred | `#[property(type = i32)]` | /// | `get [= expr]` | Specify that the property is readable and use [`PropertyGet::get`] [or optionally set a custom internal getter] | | `#[property(get)]`, `#[property(get = get_prop)]`, or `[property(get = \|_\| 2)]` | /// | `set [= expr]` | Specify that the property is writable and use [`PropertySet::set`] [or optionally set a custom internal setter] | | `#[property(set)]`, `#[property(set = set_prop)]`, or `[property(set = \|_, val\| {})]` | /// | `override_class = expr` | The type of class of which to override the property from | | `#[property(override_class = SomeClass)]` | /// | `override_interface = expr` | The type of interface of which to override the property from | | `#[property(override_interface = SomeInterface)]` | /// | `nullable` | Whether to use `Option` in the generated setter method | | `#[property(nullable)]` | /// | `member = ident` | Field of the nested type where property is retrieved and set | | `#[property(member = author)]` | /// | `construct` | Specify that the property is construct property. Ensures that the property is always set during construction (if not explicitly then the default value is used). The use of a custom internal setter is supported. | | `#[property(get, construct)]` or `#[property(get, set = set_prop, construct)]` | /// | `construct_only` | Specify that the property is construct only. This will not generate a public setter and only allow the property to be set during object construction. The use of a custom internal setter is supported. | | `#[property(get, construct_only)]` or `#[property(get, set = set_prop, construct_only)]` | /// | `builder()[.ident]*` | Used to input required params or add optional Param Spec builder fields | | `#[property(builder(SomeEnum::default()))]`, `#[builder().default_value(1).minimum(0).maximum(5)]`, etc. | /// | `default` | Sets the `default_value` field of the Param Spec builder | | `#[property(default = 1)]` | /// | ` = expr` | Used to add optional Param Spec builder fields | | `#[property(minimum = 0)` , `#[property(minimum = 0, maximum = 1)]`, etc. | /// | `` | Used to add optional Param Spec builder fields | | `#[property(explicit_notify)]` , `#[property(construct_only)]`, etc. | /// /// ## Using Rust keywords as property names /// You might hit a roadblock when declaring properties with this macro because you want to use a name that happens to be a Rust keyword. This may happen with names like `loop`, which is a pretty common name when creating things like animation handlers. /// To use those names, you can make use of the raw identifier feature of Rust. Simply prefix the identifier name with `r#` in the struct declaration. Internally, those `r#`s are stripped so you can use its expected name in [`ObjectExt::property`] or within GtkBuilder template files. /// /// # Generated methods /// The following methods are generated on the wrapper type specified on `#[properties(wrapper_type = ...)]`: /// * `$property()`, when the property is readable /// * `set_$property()`, when the property is writable and not construct-only /// * `connect_$property_notify()` /// * `notify_$property()` /// /// ## Extension trait /// You can choose to move the method definitions to a trait by using `#[properties(wrapper_type = super::MyType, ext_trait = MyTypePropertiesExt)]`. /// The trait name is optional, and defaults to `MyTypePropertiesExt`, where `MyType` is extracted from the wrapper type. /// Note: The trait is defined in the same module where the `#[derive(Properties)]` call happens, and is implemented on the wrapper type. /// /// Notice: You can't reimplement the generated methods on the wrapper type, unless you move them to a trait. /// You can change the behavior of the generated getter/setter methods by using a custom internal getter/setter. /// /// # Internal getters and setters /// By default, they are generated for you. However, you can use a custom getter/setter /// by assigning an expression to `get`/`set` `#[property]` attributes: `#[property(get = |_| 2, set)]` or `#[property(get, set = custom_setter_func)]`. /// /// # Supported types /// Every type implementing the trait [`Property`] is supported. /// The type `Option` is supported as a property only if `Option` implements [`ToValueOptional`]. /// Optional types also require the `nullable` attribute: without it, the generated setter on the wrapper type /// will take `T` instead of `Option`, preventing the user from ever calling the setter with a `None` value. /// /// ## Adding support for custom types /// ### Types wrapping an existing T: [ToValue] + [HasParamSpec] /// If you have declared a newtype as /// ```rust /// struct MyInt(i32); /// ``` /// you can use it as a property by deriving [`ValueDelegate`]. /// /// ### Types with inner mutability /// The trait [`Property`] must be implemented. /// The traits [`PropertyGet`] and [`PropertySet`] should be implemented to enable the Properties macro /// to generate a default internal getter/setter. /// If possible, implementing [`PropertySetNested`] is preferred over `PropertySet`, because it /// enables this macro to access the contained type and provide access to its fields, /// using the `member = $structfield` syntax. /// /// ### Types without [`HasParamSpec`][HasParamSpec] /// If you have encountered a type T: [ToValue], inside the gtk-rs crate, which doesn't implement [`HasParamSpec`][HasParamSpec], /// then it's a bug and you should report it. /// If you need to support a `ToValue` type with a [`ParamSpec`] not provided by gtk-rs, then you need to /// implement `HasParamSpec` on that type. /// /// # Example /// ``` /// use std::cell::RefCell; /// use glib::prelude::*; /// use glib::subclass::prelude::*; /// use glib_macros::Properties; /// /// #[derive(Default, Clone)] /// struct Author { /// name: String, /// nick: String, /// } /// /// pub mod imp { /// use std::rc::Rc; /// /// use super::*; /// /// #[derive(Properties, Default)] /// #[properties(wrapper_type = super::Foo)] /// pub struct Foo { /// #[property(get, set = Self::set_fizz)] /// fizz: RefCell, /// #[property(name = "author-name", get, set, type = String, member = name)] /// #[property(name = "author-nick", get, set, type = String, member = nick)] /// author: RefCell, /// #[property(get, set, explicit_notify, lax_validation)] /// custom_flags: RefCell, /// #[property(get, set, minimum = 0, maximum = 3)] /// numeric_builder: RefCell, /// #[property(get, set, builder('c'))] /// builder_with_required_param: RefCell, /// #[property(get, set, nullable)] /// optional: RefCell>, /// #[property(get, set)] /// smart_pointer: Rc>, /// } /// /// #[glib::derived_properties] /// impl ObjectImpl for Foo {} /// /// #[glib::object_subclass] /// impl ObjectSubclass for Foo { /// const NAME: &'static str = "MyFoo"; /// type Type = super::Foo; /// } /// /// impl Foo { /// fn set_fizz(&self, value: String) { /// *self.fizz.borrow_mut() = format!("custom set: {}", value); /// } /// } /// } /// /// glib::wrapper! { /// pub struct Foo(ObjectSubclass); /// } /// /// fn main() { /// let myfoo: Foo = glib::object::Object::new(); /// /// myfoo.set_fizz("test value"); /// assert_eq!(myfoo.fizz(), "custom set: test value".to_string()); /// } /// ``` /// /// [`Property`]: ../glib/property/trait.Property.html /// [`PropertyGet`]: ../glib/property/trait.PropertyGet.html /// [`PropertyGet::get`]: ../glib/property/trait.PropertyGet.html#tymethod.get /// [`PropertySet`]: ../glib/property/trait.PropertySet.html /// [`PropertySet::set`]: ../glib/property/trait.PropertySet.html#tymethod.set /// [`PropertySetNested`]: ../glib/property/trait.PropertySetNested.html /// [`ObjectExt::property`]: ../glib/object/trait.ObjectExt.html#tymethod.property /// [HasParamSpec]: ../glib/trait.HasParamSpec.html /// [`ParamSpec`]: ../glib/struct.ParamSpec.html /// [`ToValueOptional`]: ../glib/value/trait.ToValueOptional.html /// [ToValue]: ../glib/value/trait.ToValue.html #[allow(clippy::needless_doctest_main)] #[proc_macro_derive(Properties, attributes(properties, property))] pub fn derive_props(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as properties::PropsMacroInput); properties::impl_derive_props(input) } /// When applied to `ObjectImpl` /// ```ignore /// #[glib::derived_properties] /// impl ObjectImpl for CustomObject /// ``` /// this macro generates /// ```ignore /// impl ObjectImpl for CustomObject { /// fn properties() -> &'static [glib::ParamSpec] { /// Self::derived_properties() /// } /// fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { /// self.derived_set_property(id, value, pspec) /// } /// fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value { /// self.derived_property(id, pspec) /// } /// } /// ``` #[proc_macro_attribute] pub fn derived_properties(_attr: TokenStream, item: TokenStream) -> TokenStream { syn::parse::(item) .map_err(|_| { syn::Error::new( Span::call_site(), derived_properties_attribute::WRONG_PLACE_MSG, ) }) .and_then(|input| derived_properties_attribute::impl_derived_properties(&input)) .unwrap_or_else(syn::Error::into_compile_error) .into() } /// # Example /// ``` /// use glib::prelude::*; /// use glib::ValueDelegate; /// /// #[derive(ValueDelegate, Debug, PartialEq)] /// struct MyInt(i32); /// /// let myv = MyInt(2); /// let convertedv = myv.to_value(); /// assert_eq!(convertedv.get::(), Ok(myv)); /// /// /// #[derive(ValueDelegate, Debug, PartialEq)] /// #[value_delegate(from = u32)] /// enum MyEnum { /// Zero, /// NotZero(u32) /// } /// /// impl From for MyEnum { /// fn from(v: u32) -> Self { /// match v { /// 0 => MyEnum::Zero, /// x => MyEnum::NotZero(x) /// } /// } /// } /// impl<'a> From<&'a MyEnum> for u32 { /// fn from(v: &'a MyEnum) -> Self { /// match v { /// MyEnum::Zero => 0, /// MyEnum::NotZero(x) => *x /// } /// } /// } /// impl From for u32 { /// fn from(v: MyEnum) -> Self { /// match v { /// MyEnum::Zero => 0, /// MyEnum::NotZero(x) => x /// } /// } /// } /// /// let myv = MyEnum::NotZero(34); /// let convertedv = myv.to_value(); /// assert_eq!(convertedv.get::(), Ok(myv)); /// /// /// // If you want your type to be usable inside an `Option`, you can derive `ToValueOptional` /// // by adding `nullable` as follows /// #[derive(ValueDelegate, Debug, PartialEq)] /// #[value_delegate(nullable)] /// struct MyString(String); /// /// let myv = Some(MyString("Hello world".to_string())); /// let convertedv = myv.to_value(); /// assert_eq!(convertedv.get::>(), Ok(myv)); /// let convertedv = None::.to_value(); /// assert_eq!(convertedv.get::>(), Ok(None::)); /// ``` #[proc_macro_derive(ValueDelegate, attributes(value_delegate))] pub fn derive_value_delegate(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as value_delegate_derive::ValueDelegateInput); value_delegate_derive::impl_value_delegate(input).unwrap() } glib-macros-0.20.4/src/object_impl_attributes/interface.rs000064400000000000000000000265371046102023000217550ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use heck::ToShoutySnakeCase; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; pub fn impl_object_interface(input: super::Input) -> TokenStream { let crate_ident = crate::utils::crate_ident_new(); let super::Input { attrs, generics, trait_path, self_ty, unsafety, items, meta_dynamic, } = input; 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, &plugin_ty, dynamic.lazy_registration, ) } else { register_object_interface_as_static(&crate_ident, &self_ty) }; let mut has_prerequisites = false; let mut has_instance = false; for item in items.iter() { if let syn::ImplItem::Type(type_) = item { let name = type_.ident.to_string(); if name == "Prerequisites" { has_prerequisites = true; } else if name == "Instance" { has_instance = true; } } } let prerequisites_opt = if has_prerequisites { None } else { Some(quote!( type Prerequisites = (); )) }; let instance_opt = if has_instance { None } else { Some(quote!( type Instance = ::std::ffi::c_void; )) }; quote! { #(#attrs)* #unsafety impl #generics #trait_path for #self_ty { #prerequisites_opt #instance_opt #(#items)* } unsafe impl #crate_ident::subclass::interface::ObjectInterfaceType for #self_ty { #[inline] fn type_() -> #crate_ident::Type { Self::register_interface() } } #register_object_interface } } // Registers the object interface as a static type. fn register_object_interface_as_static( crate_ident: &TokenStream, self_ty: &syn::Ident, ) -> TokenStream { // registers the interface on first use (lazy registration). quote! { impl #self_ty { /// Registers the interface only once. #[inline] fn register_interface() -> #crate_ident::Type { static TYPE: ::std::sync::OnceLock<#crate_ident::Type> = ::std::sync::OnceLock::new(); *TYPE.get_or_init(|| unsafe { #crate_ident::subclass::register_interface::() }) } } } } // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). // An object interface can be reregistered as a dynamic type. fn register_object_interface_as_dynamic( crate_ident: &TokenStream, self_ty: &syn::Ident, plugin_ty: &TokenStream, lazy_registration: bool, ) -> TokenStream { // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). // An object interface can be reregistered as a dynamic type. if lazy_registration { // registers the object interface as a dynamic type on the first use (lazy registration). // a weak reference on the plugin is stored and will be used later on the first use of the object interface. // this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the object interface has been registered. // the registration status type. let registration_status_type = format_ident!("{}RegistrationStatus", self_ty); // name of the static variable to store the registration status. let registration_status = format_ident!( "{}", registration_status_type.to_string().to_shouty_snake_case() ); quote! { /// The registration status type: a tuple of the weak reference on the plugin and of the GLib type. struct #registration_status_type(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type); unsafe impl Send for #registration_status_type {} /// The registration status protected by a mutex guarantees so that no other threads are concurrently accessing the data. static #registration_status: ::std::sync::Mutex> = ::std::sync::Mutex::new(None); impl #self_ty { /// Registers the object interface as a dynamic type within the plugin only once. /// Plugin must have been used at least once. /// Do nothing if plugin has never been used or if the object interface is already registered as a dynamic type. #[inline] fn register_interface() -> #crate_ident::Type { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so the object interface cannot be registered as a dynamic type. None => #crate_ident::Type::INVALID, // plugin has been used and the object interface has not been registered yet, so registers it as a dynamic type. Some(#registration_status_type(type_plugin, type_)) if !type_.is_valid() => { *type_ = #crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(&(type_plugin.upgrade().unwrap())); *type_ }, // plugin has been used and the object interface has already been registered as a dynamic type. Some(#registration_status_type(_, type_)) => *type_ } } /// Depending on the plugin lifecycle state and on the registration status of the object interface: /// If plugin is used (and has loaded the implementation) for the first time, postpones the registration and stores a weak reference on the plugin. /// If plugin is reused (and has reloaded the implementation) and the object interface has been already registered as a dynamic type, reregisters it. /// An object interface can be reregistered several times as a dynamic type. /// If plugin is reused (and has reloaded the implementation) and the object interface has not been registered yet as a dynamic type, do nothing. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used (this is the first time), so postpones registration of the object interface as a dynamic type on the first use. None => { *registration_status = Some(#registration_status_type(#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); true }, // plugin has been used at least one time and the object interface has been registered as a dynamic type at least one time, so re-registers it. Some(#registration_status_type(_, type_)) if type_.is_valid() => { *type_ = #crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(type_plugin); type_.is_valid() }, // plugin has been used at least one time but the object interface has not been registered yet as a dynamic type, so keeps postponed registration. Some(_) => { true } } } /// Depending on the plugin lifecycle state and on the registration status of the object interface: /// If plugin has been used (or reused) but the object interface has not been registered yet as a dynamic type, cancels the postponed registration by deleting the weak reference on the plugin. /// Else do nothing. #[inline] pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so unload implementation is unexpected. None => false, // plugin has been used at least one time and the object interface has been registered as a dynamic type at least one time. Some(#registration_status_type(_, type_)) if type_.is_valid() => true, // plugin has been used at least one time but the object interface has not been registered yet as a dynamic type, so cancels the postponed registration. Some(_) => { *registration_status = None; true } } } } } } else { // registers immediately the object interface as a dynamic type. // name of the static variable to store the GLib type. let gtype_status = format_ident!("{}_G_TYPE", self_ty.to_string().to_shouty_snake_case()); quote! { /// The GLib type which can be safely shared between threads. static #gtype_status: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); impl #self_ty { /// Do nothing as the object interface has been registered on implementation load. #[inline] fn register_interface() -> #crate_ident::Type { let gtype = #gtype_status.load(::std::sync::atomic::Ordering::Acquire); unsafe { <#crate_ident::Type as #crate_ident::translate::FromGlib<#crate_ident::ffi::GType>>::from_glib(gtype) } } /// Registers the object interface as a dynamic type within the plugin. /// The object interface can be registered several times as a dynamic type. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { let gtype = #crate_ident::translate::IntoGlib::into_glib(#crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(type_plugin)); #gtype_status.store(gtype, ::std::sync::atomic::Ordering::Release); gtype != #crate_ident::gobject_ffi::G_TYPE_INVALID } /// Do nothing as object interfaces registered as dynamic types are never unregistered. #[inline] pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { true } } } } } glib-macros-0.20.4/src/object_impl_attributes/subclass.rs000064400000000000000000000326301046102023000216230ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use heck::ToShoutySnakeCase; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; pub fn impl_object_subclass(input: super::Input) -> TokenStream { let crate_ident = crate::utils::crate_ident_new(); let super::Input { attrs, generics, trait_path, self_ty, unsafety, items, meta_dynamic, } = input; 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; let mut has_parent_type = false; let mut has_interfaces = false; let mut has_instance = false; let mut has_class = false; for item in items.iter() { match item { syn::ImplItem::Fn(method) => { let name = &method.sig.ident; if name == "new" || name == "with_class" { has_new = true; } } syn::ImplItem::Type(type_) => { let name = &type_.ident; if name == "ParentType" { has_parent_type = true; } else if name == "Interfaces" { has_interfaces = true; } else if name == "Instance" { has_instance = true; } else if name == "Class" { has_class = true; } } _ => {} } } let parent_type_opt = (!has_parent_type).then(|| { quote!( type ParentType = #crate_ident::Object; ) }); let interfaces_opt = (!has_interfaces).then(|| { quote!( type Interfaces = (); ) }); let new_opt = (!has_new).then(|| { quote! { #[inline] fn new() -> Self { ::std::default::Default::default() } } }); let class_opt = (!has_class) .then(|| quote!(type Class = #crate_ident::subclass::basic::ClassStruct;)); let instance_opt = (!has_instance) .then(|| quote!(type Instance = #crate_ident::subclass::basic::InstanceStruct;)); quote! { #(#attrs)* #unsafety impl #generics #trait_path for #self_ty { #parent_type_opt #interfaces_opt #class_opt #instance_opt #new_opt #(#items)* } unsafe impl #crate_ident::subclass::types::ObjectSubclassType for #self_ty { #[inline] fn type_data() -> ::std::ptr::NonNull<#crate_ident::subclass::TypeData> { static mut DATA: #crate_ident::subclass::TypeData = #crate_ident::subclass::types::TypeData::new(); unsafe { ::std::ptr::NonNull::new_unchecked(::std::ptr::addr_of_mut!(DATA)) } } #[inline] fn type_() -> #crate_ident::Type { Self::register_type(); unsafe { let data = Self::type_data(); let type_ = data.as_ref().type_(); type_ } } } #register_object_subclass #[doc(hidden)] impl #crate_ident::subclass::types::FromObject for #self_ty { type FromObjectType = ::Type; #[inline] fn from_object(obj: &Self::FromObjectType) -> &Self { ::from_obj(obj) } } #[doc(hidden)] impl #crate_ident::clone::Downgrade for #self_ty { type Weak = #crate_ident::subclass::ObjectImplWeakRef<#self_ty>; #[inline] fn downgrade(&self) -> Self::Weak { let ref_counted = #crate_ident::subclass::prelude::ObjectSubclassExt::ref_counted(self); #crate_ident::clone::Downgrade::downgrade(&ref_counted) } } impl #self_ty { #[inline] pub fn downgrade(&self) -> ::Weak { #crate_ident::clone::Downgrade::downgrade(self) } } #[doc(hidden)] impl ::std::borrow::ToOwned for #self_ty { type Owned = #crate_ident::subclass::ObjectImplRef<#self_ty>; #[inline] fn to_owned(&self) -> Self::Owned { #crate_ident::subclass::prelude::ObjectSubclassExt::ref_counted(self) } } #[doc(hidden)] impl ::std::borrow::Borrow<#self_ty> for #crate_ident::subclass::ObjectImplRef<#self_ty> { #[inline] fn borrow(&self) -> &#self_ty { self } } } } // Registers the object subclass as a static type. fn register_object_subclass_as_static( crate_ident: &TokenStream, self_ty: &syn::Ident, ) -> TokenStream { // registers the object subclass on first use (lazy registration). quote! { impl #self_ty { /// Registers the type only once. #[inline] fn register_type() { static ONCE: ::std::sync::Once = ::std::sync::Once::new(); ONCE.call_once(|| { #crate_ident::subclass::register_type::(); }) } } } } // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). // An object subclass can be reregistered as a dynamic type. fn register_object_subclass_as_dynamic( crate_ident: &TokenStream, self_ty: &syn::Ident, plugin_ty: &TokenStream, lazy_registration: bool, ) -> TokenStream { // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). // An object subclass can be reregistered as a dynamic type. if lazy_registration { // registers the object subclass as a dynamic type on the first use (lazy registration). // a weak reference on the plugin is stored and will be used later on the first use of the object subclass. // this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the object subclass has been registered. // the registration status type. let registration_status_type = format_ident!("{}RegistrationStatus", self_ty); // name of the static variable to store the registration status. let registration_status = format_ident!( "{}", registration_status_type.to_string().to_shouty_snake_case() ); quote! { /// The registration status type: a tuple of the weak reference on the plugin and of the GLib type. struct #registration_status_type(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type); unsafe impl Send for #registration_status_type {} /// The registration status protected by a mutex guarantees so that no other threads are concurrently accessing the data. static #registration_status: ::std::sync::Mutex> = ::std::sync::Mutex::new(None); impl #self_ty { /// Registers the object subclass as a dynamic type within the plugin only once. /// Plugin must have been used at least once. /// Do nothing if plugin has never been used or if the object subclass is already registered as a dynamic type. #[inline] fn register_type() { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so the object subclass cannot be registered as a dynamic type. None => (), // plugin has been used and the object subclass has not been registered yet, so registers it as a dynamic type. Some(#registration_status_type(type_plugin, type_)) if !type_.is_valid() => { *type_ = #crate_ident::subclass::register_dynamic_type::<#plugin_ty, Self>(&(type_plugin.upgrade().unwrap())); }, // plugin has been used and the object subclass has already been registered as a dynamic type. Some(_) => () } } /// Depending on the plugin lifecycle state and on the registration status of the object subclass: /// If plugin is used (and has loaded the implementation) for the first time, postpones the registration and stores a weak reference on the plugin. /// If plugin is reused (and has reloaded the implementation) and the object subclass has been already registered as a dynamic type, reregisters it. /// An object subclass can be reregistered several times as a dynamic type. /// If plugin is reused (and has reloaded the implementation) and the object subclass has not been registered yet as a dynamic type, do nothing. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used (this is the first time), so postpones registration of the object subclass as a dynamic type on the first use. None => { *registration_status = Some(#registration_status_type(#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); true }, // plugin has been used at least one time and the object subclass has been registered as a dynamic type at least one time, so re-registers it. Some(#registration_status_type(_, type_)) if type_.is_valid() => { *type_ = #crate_ident::subclass::register_dynamic_type::<#plugin_ty, Self>(type_plugin); type_.is_valid() }, // plugin has been used at least one time but the object subclass has not been registered yet as a dynamic type, so keeps postponed registration. Some(_) => { true } } } /// Depending on the plugin lifecycle state and on the registration status of the object subclass: /// If plugin has been used (or reused) but the object subclass has not been registered yet as a dynamic type, cancels the postponed registration by deleting the weak reference on the plugin. /// Else do nothing. #[inline] pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so unload implementation is unexpected. None => false, // plugin has been used at least one time and the object subclass has been registered as a dynamic type at least one time. Some(#registration_status_type(_, type_)) if type_.is_valid() => true, // plugin has been used at least one time but the object subclass has not been registered yet as a dynamic type, so cancels the postponed registration. Some(_) => { *registration_status = None; true } } } } } } else { // registers immediately the object subclass as a dynamic type. quote! { impl #self_ty { /// Do nothing as the object subclass has been registered on implementation load. #[inline] fn register_type() { } /// Registers the object subclass as a dynamic type within the plugin. /// The object subclass can be registered several times as a dynamic type. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { let type_ = #crate_ident::subclass::register_dynamic_type::<#plugin_ty, Self>(type_plugin); type_ != #crate_ident::Type::INVALID } /// Do nothing as object subclasses registered as dynamic types are never unregistered. #[inline] pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { true } } } } } glib-macros-0.20.4/src/object_impl_attributes.rs000064400000000000000000000102341046102023000200000ustar 00000000000000// 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) } } } glib-macros-0.20.4/src/properties.rs000064400000000000000000000647311046102023000154520ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use crate::utils::crate_ident_new; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use quote::format_ident; use quote::{quote, quote_spanned}; use std::collections::HashMap; use syn::ext::IdentExt; use syn::parenthesized; use syn::parse::Parse; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::Token; use syn::{parse_quote_spanned, LitStr}; pub struct PropsMacroInput { wrapper_ty: syn::Path, ext_trait: Option>, ident: syn::Ident, props: Vec, } pub struct PropertiesAttrs { wrapper_ty: syn::Path, // None => no ext trait, // Some(None) => derive the ext trait from the wrapper type, // Some(Some(ident)) => use the given ext trait Ident ext_trait: Option>, } impl Parse for PropertiesAttrs { fn parse(input: syn::parse::ParseStream) -> syn::Result { let mut wrapper_ty = None; let mut ext_trait = None; while !input.is_empty() { let ident = input.parse::()?; if ident == "wrapper_type" { let _eq = input.parse::()?; wrapper_ty = Some(input.parse::()?); } else if ident == "ext_trait" { if input.peek(Token![=]) { let _eq = input.parse::()?; let ident = input.parse::()?; ext_trait = Some(Some(ident)); } else { ext_trait = Some(None); } } if input.peek(Token![,]) { input.parse::()?; } } Ok(Self { wrapper_ty: wrapper_ty.ok_or_else(|| { syn::Error::new(input.span(), "missing #[properties(wrapper_type = ...)]") })?, ext_trait, }) } } impl Parse for PropsMacroInput { fn parse(input: syn::parse::ParseStream) -> syn::Result { let derive_input: syn::DeriveInput = input.parse()?; let attrs = derive_input .attrs .iter() .find(|x| x.path().is_ident("properties")) .ok_or_else(|| { syn::Error::new( derive_input.span(), "missing #[properties(wrapper_type = ...)]", ) })?; let attrs: PropertiesAttrs = attrs.parse_args()?; let props: Vec<_> = match derive_input.data { syn::Data::Struct(struct_data) => parse_fields(struct_data.fields)?, _ => { return Err(syn::Error::new( derive_input.span(), "Properties can only be derived on structs", )) } }; Ok(Self { wrapper_ty: attrs.wrapper_ty, ext_trait: attrs.ext_trait, ident: derive_input.ident, props, }) } } enum MaybeCustomFn { Custom(Box), Default, } impl std::convert::From> for MaybeCustomFn { fn from(item: Option) -> Self { match item { Some(expr) => Self::Custom(Box::new(expr)), None => Self::Default, } } } enum PropAttr { // builder(required_params).parameter(value) // becomes // Builder(Punctuated(required_params), Optionals(TokenStream)) Builder(Punctuated, TokenStream2), // ident Nullable, // ident [= expr] Get(Option), Set(Option), // ident = expr OverrideClass(syn::Type), OverrideInterface(syn::Type), // ident = expr Type(syn::Type), // This will get translated from `ident = value` to `.ident(value)` // and will get appended after the `builder(...)` call. // ident [= expr] BuilderField((syn::Ident, Option)), // ident = ident Member(syn::Ident), // ident = "literal" Name(syn::LitStr), } impl Parse for PropAttr { fn parse(input: syn::parse::ParseStream) -> syn::Result { let name = input.call(syn::Ident::parse_any)?; let name_str = name.to_string(); let res = if input.peek(Token![=]) { let _assign_token: Token![=] = input.parse()?; // name = expr | type | ident match &*name_str { "name" => PropAttr::Name(input.parse()?), "get" => PropAttr::Get(Some(input.parse()?)), "set" => PropAttr::Set(Some(input.parse()?)), "override_class" => PropAttr::OverrideClass(input.parse()?), "override_interface" => PropAttr::OverrideInterface(input.parse()?), "type" => PropAttr::Type(input.parse()?), "member" => PropAttr::Member(input.parse()?), // Special case "default = ..." and map it to .default_value(...) "default" => PropAttr::BuilderField(( syn::Ident::new("default_value", name.span()), Some(input.parse()?), )), _ => PropAttr::BuilderField((name, Some(input.parse()?))), } } else if input.peek(syn::token::Paren) { match &*name_str { "builder" => { let content; parenthesized!(content in input); let required = content.parse_terminated(syn::Expr::parse, Token![,])?; let rest: TokenStream2 = input.parse()?; PropAttr::Builder(required, rest) } _ => { return Err(syn::Error::new( name.span(), format!("Unsupported attribute list {name_str}(...)"), )) } } } else { // attributes with only the identifier name match &*name_str { "nullable" => PropAttr::Nullable, "get" => PropAttr::Get(None), "set" => PropAttr::Set(None), "readwrite" | "read_only" | "write_only" => { return Err(syn::Error::new( name.span(), format!( "{name} is a flag managed by the Properties macro. \ Use `get` and `set` to manage read and write access to a property", ), )) } _ => PropAttr::BuilderField((name, None)), } }; Ok(res) } } #[derive(Default)] struct ReceivedAttrs { nullable: bool, get: Option, set: Option, override_class: Option, override_interface: Option, ty: Option, member: Option, name: Option, builder: Option<(Punctuated, TokenStream2)>, builder_fields: HashMap>, } impl Parse for ReceivedAttrs { fn parse(input: syn::parse::ParseStream) -> syn::Result { let attrs = syn::punctuated::Punctuated::::parse_terminated(input)?; let this = attrs.into_iter().fold(Self::default(), |mut this, attr| { this.set_from_attr(attr); this }); Ok(this) } } impl ReceivedAttrs { fn set_from_attr(&mut self, attr: PropAttr) { match attr { PropAttr::Nullable => self.nullable = true, PropAttr::Get(some_fn) => self.get = Some(some_fn.into()), PropAttr::Set(some_fn) => self.set = Some(some_fn.into()), PropAttr::Name(lit) => self.name = Some(lit), PropAttr::OverrideClass(ty) => self.override_class = Some(ty), PropAttr::OverrideInterface(ty) => self.override_interface = Some(ty), PropAttr::Type(ty) => self.ty = Some(ty), PropAttr::Member(member) => self.member = Some(member), PropAttr::Builder(required_params, optionals) => { self.builder = Some((required_params, optionals)) } PropAttr::BuilderField((ident, expr)) => { self.builder_fields.insert(ident, expr); } } } } // It's a cleaned up version of `ReceivedAttrs` where some missing attributes get a default, // generated value. struct PropDesc { attrs_span: proc_macro2::Span, field_ident: syn::Ident, ty: syn::Type, name: syn::LitStr, override_class: Option, override_interface: Option, nullable: bool, get: Option, set: Option, member: Option, builder: Option<(Punctuated, TokenStream2)>, builder_fields: HashMap>, is_construct_only: bool, } impl PropDesc { fn new( attrs_span: proc_macro2::Span, field_ident: syn::Ident, field_ty: syn::Type, attrs: ReceivedAttrs, ) -> syn::Result { let ReceivedAttrs { nullable, get, mut set, override_class, override_interface, ty, member, name, builder, builder_fields, } = attrs; let is_construct_only = builder_fields.iter().any(|(k, _)| *k == "construct_only"); if is_construct_only && set.is_none() { // Insert a default internal setter automatically set = Some(MaybeCustomFn::Default); } if get.is_none() && set.is_none() { return Err(syn::Error::new( attrs_span, "No `get` or `set` specified: at least one is required.".to_string(), )); } if override_class.is_some() && override_interface.is_some() { return Err(syn::Error::new( attrs_span, "Both `override_class` and `override_interface` specified.".to_string(), )); } // Fill needed, but missing, attributes with calculated default values let name = name.unwrap_or_else(|| { syn::LitStr::new( &field_ident.to_string().trim_matches('_').replace('_', "-"), field_ident.span(), ) }); let ty = ty.unwrap_or_else(|| field_ty.clone()); // Now that everything is set and safe, return the final property description Ok(Self { attrs_span, field_ident, ty, name, override_class, override_interface, nullable, get, set, member, builder, builder_fields, is_construct_only, }) } fn is_overriding(&self) -> bool { self.override_class.is_some() || self.override_interface.is_some() } } fn expand_param_spec(prop: &PropDesc) -> TokenStream2 { let crate_ident = crate_ident_new(); let PropDesc { ty, name, builder, .. } = prop; let stripped_name = strip_raw_prefix_from_name(name); match (&prop.override_class, &prop.override_interface) { (Some(c), None) => { return quote!(#crate_ident::ParamSpecOverride::for_class::<#c>(#stripped_name)) } (None, Some(i)) => { return quote!(#crate_ident::ParamSpecOverride::for_interface::<#i>(#stripped_name)) } (Some(_), Some(_)) => { unreachable!("Both `override_class` and `override_interface` specified") } (None, None) => (), }; let rw_flags = match (&prop.get, &prop.set) { (Some(_), Some(_)) => quote!(.readwrite()), (Some(_), None) => quote!(.read_only()), (None, Some(_)) => quote!(.write_only()), (None, None) => unreachable!("No `get` or `set` specified"), }; let builder_call = builder .as_ref() .cloned() .map(|(mut required_params, chained_methods)| { let name_expr = syn::ExprLit { attrs: vec![], lit: syn::Lit::Str(stripped_name.to_owned()), }; required_params.insert(0, name_expr.into()); let required_params = required_params.iter(); quote!((#(#required_params,)*)#chained_methods) }) .unwrap_or(quote!((#stripped_name))); let builder_fields = prop.builder_fields.iter().map(|(k, v)| quote!(.#k(#v))); let span = prop.attrs_span; quote_spanned! {span=> <<#ty as #crate_ident::property::Property>::Value as #crate_ident::prelude::HasParamSpec> ::param_spec_builder() #builder_call #rw_flags #(#builder_fields)* .build() } } fn expand_properties_fn(props: &[PropDesc]) -> TokenStream2 { let n_props = props.len(); let crate_ident = crate_ident_new(); let param_specs = props.iter().map(expand_param_spec); quote!( fn derived_properties() -> &'static [#crate_ident::ParamSpec] { use #crate_ident::prelude::ParamSpecBuilderExt; static PROPERTIES: ::std::sync::OnceLock<[#crate_ident::ParamSpec; #n_props]> = ::std::sync::OnceLock::new(); PROPERTIES.get_or_init(|| [ #(#param_specs,)* ]) } ) } fn expand_property_fn(props: &[PropDesc]) -> TokenStream2 { let crate_ident = crate_ident_new(); let match_branch_get = props.iter().flat_map(|p| { let PropDesc { name, field_ident, member, get, ty, .. } = p; let enum_ident = name_to_enum_ident(name.value()); let span = p.attrs_span; get.as_ref().map(|get| { let body = match (member, get) { (_, MaybeCustomFn::Custom(expr)) => quote!( DerivedPropertiesEnum::#enum_ident => { let value: <#ty as #crate_ident::property::Property>::Value = (#expr)(&self); ::std::convert::From::from(value) } ), (None, MaybeCustomFn::Default) => quote!( DerivedPropertiesEnum::#enum_ident => #crate_ident::property::PropertyGet::get(&self.#field_ident, |v| ::std::convert::From::from(v)) ), (Some(member), MaybeCustomFn::Default) => quote!( DerivedPropertiesEnum::#enum_ident => #crate_ident::property::PropertyGet::get(&self.#field_ident, |v| ::std::convert::From::from(&v.#member)) ), }; quote_spanned!(span=> #body) }) }); quote!( fn derived_property( &self, id: usize, pspec: &#crate_ident::ParamSpec ) -> #crate_ident::Value { let prop: DerivedPropertiesEnum = std::convert::TryFrom::try_from(id-1) .unwrap_or_else(|_| panic!("property not defined {}", pspec.name())); match prop { #(#match_branch_get,)* _ => panic!("missing getter for property {}", pspec.name()), } } ) } fn expand_set_property_fn(props: &[PropDesc]) -> TokenStream2 { let crate_ident = crate_ident_new(); let match_branch_set = props.iter().flat_map(|p| { let PropDesc { name, field_ident, member, set, ty, .. } = p; let stripped_name = strip_raw_prefix_from_name(name); let crate_ident = crate_ident_new(); let enum_ident = name_to_enum_ident(name.value()); let span = p.attrs_span; let expect = quote!(.unwrap_or_else( |err| panic!( "Invalid conversion from `glib::value::Value` to `{}` inside setter for property `{}`: {:?}", ::std::any::type_name::<<#ty as #crate_ident::property::Property>::Value>(), #stripped_name, err ) )); set.as_ref().map(|set| { let body = match (member, set) { (_, MaybeCustomFn::Custom(expr)) => quote!( DerivedPropertiesEnum::#enum_ident => { (#expr)(&self, #crate_ident::Value::get(value)#expect); } ), (None, MaybeCustomFn::Default) => quote!( DerivedPropertiesEnum::#enum_ident => { #crate_ident::property::PropertySet::set( &self.#field_ident, #crate_ident::Value::get(value)#expect ); } ), (Some(member), MaybeCustomFn::Default) => quote!( DerivedPropertiesEnum::#enum_ident => { #crate_ident::property::PropertySetNested::set_nested( &self.#field_ident, move |v| v.#member = #crate_ident::Value::get(value)#expect ); } ), }; quote_spanned!(span=> #body) }) }); quote!( #[allow(unreachable_code)] fn derived_set_property(&self, id: usize, value: &#crate_ident::Value, pspec: &#crate_ident::ParamSpec ){ let prop: DerivedPropertiesEnum = std::convert::TryFrom::try_from(id-1) .unwrap_or_else(|_| panic!("property not defined {}", pspec.name())); match prop { #(#match_branch_set,)* _ => panic!("missing setter for property {}", pspec.name()), } } ) } fn parse_fields(fields: syn::Fields) -> syn::Result> { fields .into_iter() .flat_map(|field| { let syn::Field { ident, attrs, ty, .. } = field; attrs .into_iter() .filter(|a| a.path().is_ident("property")) .map(move |prop_attrs| { let span = prop_attrs.span(); PropDesc::new( span, ident.as_ref().unwrap().clone(), ty.clone(), prop_attrs.parse_args()?, ) }) }) .collect::>() } /// Converts a glib property name to a correct rust ident fn name_to_ident(name: &syn::LitStr) -> syn::Ident { format_ident!("{}", name.value().replace('-', "_")) } /// Strips out raw identifier prefix (`r#`) from literal string items fn strip_raw_prefix_from_name(name: &LitStr) -> LitStr { LitStr::new( name.value().strip_prefix("r#").unwrap_or(&name.value()), name.span(), ) } fn expand_impl_getset_properties(props: &[PropDesc]) -> Vec { let crate_ident = crate_ident_new(); let defs = props.iter().filter(|p| !p.is_overriding()).map(|p| { let name = &p.name; let stripped_name = strip_raw_prefix_from_name(name); let ident = name_to_ident(name); let ty = &p.ty; let getter = p.get.is_some().then(|| { let span = p.attrs_span; parse_quote_spanned!(span=> #[must_use] #[allow(dead_code)] pub fn #ident(&self) -> <#ty as #crate_ident::property::Property>::Value { self.property::<<#ty as #crate_ident::property::Property>::Value>(#stripped_name) } ) }); let setter = (p.set.is_some() && !p.is_construct_only).then(|| { let ident = format_ident!("set_{}", ident); let target_ty = quote!(<<#ty as #crate_ident::property::Property>::Value as #crate_ident::prelude::HasParamSpec>::SetValue); let set_ty = if p.nullable { quote!(::core::option::Option>) } else { quote!(impl std::borrow::Borrow<#target_ty>) }; let upcasted_borrowed_value = if p.nullable { quote!( value.as_ref().map(|v| std::borrow::Borrow::borrow(v)) ) } else { quote!( std::borrow::Borrow::borrow(&value) ) }; let span = p.attrs_span; parse_quote_spanned!(span=> #[allow(dead_code)] pub fn #ident<'a>(&self, value: #set_ty) { self.set_property_from_value(#stripped_name, &::std::convert::From::from(#upcasted_borrowed_value)) } ) }); [getter, setter] }); defs.flatten() // flattens [] .flatten() // removes None .collect::>() } fn expand_impl_connect_prop_notify(props: &[PropDesc]) -> Vec { let crate_ident = crate_ident_new(); let connection_fns = props.iter().filter(|p| !p.is_overriding()).map(|p| -> syn::ImplItemFn { let name = &p.name; let stripped_name = strip_raw_prefix_from_name(name); let fn_ident = format_ident!("connect_{}_notify", name_to_ident(name)); let span = p.attrs_span; parse_quote_spanned!(span=> #[allow(dead_code)] pub fn #fn_ident(&self, f: F) -> #crate_ident::SignalHandlerId { self.connect_notify_local(::core::option::Option::Some(#stripped_name), move |this, _| { f(this) }) } ) }); connection_fns.collect::>() } fn expand_impl_notify_prop(wrapper_type: &syn::Path, props: &[PropDesc]) -> Vec { let crate_ident = crate_ident_new(); let emit_fns = props.iter().filter(|p| !p.is_overriding()).map(|p| -> syn::ImplItemFn { let name = strip_raw_prefix_from_name(&p.name); let fn_ident = format_ident!("notify_{}", name_to_ident(&name)); let span = p.attrs_span; let enum_ident = name_to_enum_ident(name.value()); parse_quote_spanned!(span=> #[allow(dead_code)] pub fn #fn_ident(&self) { self.notify_by_pspec( &<<#wrapper_type as #crate_ident::object::ObjectSubclassIs>::Subclass as #crate_ident::subclass::object::DerivedObjectProperties>::derived_properties() [DerivedPropertiesEnum::#enum_ident as usize] ); } ) }); emit_fns.collect::>() } fn name_to_enum_ident(name: String) -> syn::Ident { let mut name = name.strip_prefix("r#").unwrap_or(&name).to_owned(); let mut slice = name.as_mut_str(); while let Some(i) = slice.find('-') { let (head, tail) = slice.split_at_mut(i); if let Some(c) = head.get_mut(0..1) { c.make_ascii_uppercase(); } slice = &mut tail[1..]; } if let Some(c) = slice.get_mut(0..1) { c.make_ascii_uppercase(); } let enum_member: String = name.split('-').collect(); format_ident!("{}", enum_member) } fn expand_properties_enum(props: &[PropDesc]) -> TokenStream2 { if props.is_empty() { quote! { #[derive(Debug, Copy, Clone)] enum DerivedPropertiesEnum {} impl std::convert::TryFrom for DerivedPropertiesEnum { type Error = usize; fn try_from(item: usize) -> ::core::result::Result>::Error> { ::core::result::Result::Err(item) } } } } else { let properties: Vec = props .iter() .map(|p| { let name: String = p.name.value(); name_to_enum_ident(name) }) .collect(); let props = properties.iter(); let indices = 0..properties.len(); quote! { #[repr(usize)] #[derive(Debug, Copy, Clone)] enum DerivedPropertiesEnum { #(#props,)* } impl std::convert::TryFrom for DerivedPropertiesEnum { type Error = usize; fn try_from(item: usize) -> ::core::result::Result>::Error> { match item { #(#indices => ::core::result::Result::Ok(Self::#properties),)* _ => ::core::result::Result::Err(item) } } } } } } pub fn impl_derive_props(input: PropsMacroInput) -> TokenStream { let struct_ident = &input.ident; let crate_ident = crate_ident_new(); let wrapper_type = input.wrapper_ty; let fn_properties = expand_properties_fn(&input.props); let fn_property = expand_property_fn(&input.props); let fn_set_property = expand_set_property_fn(&input.props); let getset_properties = expand_impl_getset_properties(&input.props); let connect_prop_notify = expand_impl_connect_prop_notify(&input.props); let notify_prop = expand_impl_notify_prop(&wrapper_type, &input.props); let properties_enum = expand_properties_enum(&input.props); let rust_interface = if let Some(ext_trait) = input.ext_trait { let trait_ident = if let Some(ext_trait) = ext_trait { ext_trait } else { format_ident!( "{}PropertiesExt", wrapper_type.segments.last().unwrap().ident ) }; let fns_without_visibility_modifier = getset_properties .into_iter() .chain(connect_prop_notify) .chain(notify_prop) .map(|mut item| { item.vis = syn::Visibility::Inherited; item }); quote! { pub trait #trait_ident: #crate_ident::prelude::IsA<#wrapper_type> { #(#fns_without_visibility_modifier)* } impl> #trait_ident for T {} } } else { quote! { #[allow(dead_code)] impl #wrapper_type { #(#getset_properties)* #(#connect_prop_notify)* #(#notify_prop)* } } }; let expanded = quote! { #properties_enum impl #crate_ident::subclass::object::DerivedObjectProperties for #struct_ident { #fn_properties #fn_property #fn_set_property } #rust_interface }; proc_macro::TokenStream::from(expanded) } glib-macros-0.20.4/src/shared_boxed_derive.rs000064400000000000000000000304161046102023000172340ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::{Ident, TokenStream}; use quote::quote; use crate::utils::{crate_ident_new, parse_nested_meta_items, NestedMetaItem}; fn gen_impl_to_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream { let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident); quote! { impl #crate_ident::value::ToValueOptional for #name { #[inline] fn to_value_optional(s: ::core::option::Option<&Self>) -> #crate_ident::Value { let mut value = #crate_ident::Value::for_value_type::(); unsafe { let ptr = match s { ::core::option::Option::Some(s) => #refcounted_type_prefix::into_raw(s.0.clone()), ::core::option::Option::None => ::std::ptr::null(), }; #crate_ident::gobject_ffi::g_value_take_boxed( #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, ptr as *mut _ ); } value } } impl #crate_ident::value::ValueTypeOptional for #name { } } } fn gen_impl_from_value_optional(name: &Ident, crate_ident: &TokenStream) -> TokenStream { let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident); quote! { unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name { type Checker = #crate_ident::value::GenericValueTypeOrNoneChecker; #[inline] unsafe fn from_value(value: &'a #crate_ident::Value) -> Self { let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0); debug_assert!(!ptr.is_null()); #name(#refcounted_type_prefix::from_raw(ptr as *mut _)) } } } } fn gen_impl_from_value(name: &Ident, crate_ident: &TokenStream) -> TokenStream { let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident); quote! { unsafe impl<'a> #crate_ident::value::FromValue<'a> for #name { type Checker = #crate_ident::value::GenericValueTypeChecker; #[inline] unsafe fn from_value(value: &'a #crate_ident::Value) -> Self { let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(#crate_ident::translate::ToGlibPtr::to_glib_none(value).0); debug_assert!(!ptr.is_null()); #name(#refcounted_type_prefix::from_raw(ptr as *mut _)) } } } } fn refcounted_type(input: &syn::DeriveInput) -> Option<&syn::TypePath> { let fields = match &input.data { syn::Data::Struct(s) => &s.fields, _ => return None, }; let unnamed = match fields { syn::Fields::Unnamed(u) if u.unnamed.len() == 1 => &u.unnamed[0], _ => return None, }; let refcounted = match &unnamed.ty { syn::Type::Path(p) => p, _ => return None, }; Some(refcounted) } fn refcounted_type_prefix(name: &Ident, crate_ident: &TokenStream) -> proc_macro2::TokenStream { quote! { <<#name as #crate_ident::subclass::shared::SharedType>::RefCountedType as #crate_ident::subclass::shared::RefCounted> } } pub fn impl_shared_boxed(input: &syn::DeriveInput) -> syn::Result { let name = &input.ident; let Some(refcounted_type) = refcounted_type(input) else { return Err(syn::Error::new_spanned( input, "#[derive(glib::SharedBoxed)] requires struct MyStruct(T: RefCounted)", )); }; let mut gtype_name = NestedMetaItem::::new("name") .required() .value_required(); let mut nullable = NestedMetaItem::::new("nullable").value_optional(); let mut allow_name_conflict = NestedMetaItem::::new("allow_name_conflict").value_optional(); let found = parse_nested_meta_items( &input.attrs, "shared_boxed_type", &mut [&mut gtype_name, &mut nullable, &mut allow_name_conflict], )?; if found.is_none() { return Err(syn::Error::new_spanned(input, "#[derive(glib::SharedBoxed)] requires #[shared_boxed_type(name = \"SharedBoxedTypeName\")]" )); } let gtype_name = gtype_name.value.unwrap(); let nullable = nullable.found || nullable.value.map(|b| b.value()).unwrap_or(false); let allow_name_conflict = allow_name_conflict.found || allow_name_conflict .value .map(|b| b.value()) .unwrap_or(false); let crate_ident = crate_ident_new(); let refcounted_type_prefix = refcounted_type_prefix(name, &crate_ident); let impl_from_value = if !nullable { gen_impl_from_value(name, &crate_ident) } else { gen_impl_from_value_optional(name, &crate_ident) }; let impl_to_value_optional = if nullable { gen_impl_to_value_optional(name, &crate_ident) } else { quote! {} }; Ok(quote! { impl #crate_ident::subclass::shared::SharedType for #name { const NAME: &'static ::core::primitive::str = #gtype_name; const ALLOW_NAME_CONFLICT: bool = #allow_name_conflict; type RefCountedType = #refcounted_type; #[inline] fn from_refcounted(this: Self::RefCountedType) -> Self { Self(this) } #[inline] fn into_refcounted(self) -> Self::RefCountedType { self.0 } } impl #crate_ident::prelude::StaticType for #name { #[inline] fn static_type() -> #crate_ident::Type { static TYPE: ::std::sync::OnceLock<#crate_ident::Type> = ::std::sync::OnceLock::new(); *TYPE.get_or_init(|| { #crate_ident::subclass::shared::register_shared_type::<#name>() }) } } impl #crate_ident::value::ValueType for #name { type Type = #name; } impl #crate_ident::value::ToValue for #name { #[inline] fn to_value(&self) -> #crate_ident::Value { unsafe { let ptr = #refcounted_type_prefix::into_raw(self.0.clone()); let mut value = #crate_ident::Value::from_type_unchecked(<#name as #crate_ident::prelude::StaticType>::static_type()); #crate_ident::gobject_ffi::g_value_take_boxed( #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, ptr as *mut _ ); value } } #[inline] fn value_type(&self) -> #crate_ident::Type { <#name as #crate_ident::prelude::StaticType>::static_type() } } impl ::std::convert::From<#name> for #crate_ident::Value { #[inline] fn from(v: #name) -> Self { unsafe { let mut value = #crate_ident::Value::from_type_unchecked(<#name as #crate_ident::prelude::StaticType>::static_type()); #crate_ident::gobject_ffi::g_value_take_boxed( #crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0, #crate_ident::translate::IntoGlibPtr::<*mut #refcounted_type_prefix::InnerType>::into_glib_ptr(v) as *mut _, ); value } } } #impl_to_value_optional #impl_from_value impl #crate_ident::translate::GlibPtrDefault for #name { type GlibType = *mut #refcounted_type_prefix::InnerType; } impl #crate_ident::translate::FromGlibPtrBorrow<*const #refcounted_type_prefix::InnerType> for #name { #[inline] unsafe fn from_glib_borrow(ptr: *const #refcounted_type_prefix::InnerType) -> #crate_ident::translate::Borrowed { debug_assert!(!ptr.is_null()); // from_raw is taking ownership of the raw pointer here, but wrapping its result // in Borrowed::new ensures that it won't be deallocated when it will go out of // scope, so the pointer will still be valid afterwards #crate_ident::translate::Borrowed::new(#name(#refcounted_type_prefix::from_raw(ptr))) } } impl #crate_ident::translate::FromGlibPtrBorrow<*mut #refcounted_type_prefix::InnerType> for #name { #[inline] unsafe fn from_glib_borrow(ptr: *mut #refcounted_type_prefix::InnerType) -> #crate_ident::translate::Borrowed { #crate_ident::translate::FromGlibPtrBorrow::from_glib_borrow(ptr as *const _) } } impl #crate_ident::translate::FromGlibPtrNone<*const #refcounted_type_prefix::InnerType> for #name { #[inline] unsafe fn from_glib_none(ptr: *const #refcounted_type_prefix::InnerType) -> Self { let ptr = #refcounted_type_prefix::ref_(ptr); #name(#refcounted_type_prefix::from_raw(ptr)) } } impl #crate_ident::translate::FromGlibPtrNone<*mut #refcounted_type_prefix::InnerType> for #name { #[inline] unsafe fn from_glib_none(ptr: *mut #refcounted_type_prefix::InnerType) -> Self { #crate_ident::translate::FromGlibPtrNone::from_glib_none(ptr as *const _) } } impl #crate_ident::translate::FromGlibPtrFull<*mut #refcounted_type_prefix::InnerType> for #name { #[inline] unsafe fn from_glib_full(ptr: *mut #refcounted_type_prefix::InnerType) -> Self { #name(#refcounted_type_prefix::from_raw(ptr)) } } impl #crate_ident::translate::IntoGlibPtr<*mut #refcounted_type_prefix::InnerType> for #name { #[inline] unsafe fn into_glib_ptr(self) -> *mut #refcounted_type_prefix::InnerType { let r = ::into_refcounted(self); #refcounted_type_prefix::into_raw(r) as *mut _ } } impl<'a> #crate_ident::translate::ToGlibPtr<'a, *const #refcounted_type_prefix::InnerType> for #name { type Storage = std::marker::PhantomData<&'a Self>; #[inline] fn to_glib_none(&'a self) -> #crate_ident::translate::Stash<'a, *const #refcounted_type_prefix::InnerType, Self> { unsafe { #crate_ident::translate::Stash(#refcounted_type_prefix::as_ptr(&self.0), std::marker::PhantomData) } } #[inline] fn to_glib_full(&self) -> *const #refcounted_type_prefix::InnerType { let r = <#name as #crate_ident::subclass::shared::SharedType>::into_refcounted(self.clone()); unsafe { #refcounted_type_prefix::into_raw(r) } } } impl<'a> #crate_ident::translate::ToGlibPtr<'a, *mut #refcounted_type_prefix::InnerType> for #name { type Storage = std::marker::PhantomData<&'a Self>; #[inline] fn to_glib_none(&'a self) -> #crate_ident::translate::Stash<'a, *mut #refcounted_type_prefix::InnerType, Self> { unsafe { #crate_ident::translate::Stash(#refcounted_type_prefix::as_ptr(&self.0) as *mut _, std::marker::PhantomData) } } #[inline] fn to_glib_full(&self) -> *mut #refcounted_type_prefix::InnerType { let r = <#name as #crate_ident::subclass::shared::SharedType>::into_refcounted(self.clone()); unsafe { #refcounted_type_prefix::into_raw(r) as *mut _ } } } impl #crate_ident::HasParamSpec for #name { type ParamSpec = #crate_ident::ParamSpecBoxed; type SetValue = Self; type BuilderFn = fn(&::core::primitive::str) -> #crate_ident::ParamSpecBoxedBuilder; fn param_spec_builder() -> Self::BuilderFn { |name| Self::ParamSpec::builder(name) } } }) } glib-macros-0.20.4/src/utils.rs000064400000000000000000000213701046102023000144060ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::{Ident, Span, TokenStream}; use proc_macro_crate::crate_name; use quote::{quote, quote_spanned, ToTokens}; use syn::{ meta::ParseNestedMeta, parse::Parse, punctuated::Punctuated, spanned::Spanned, token::Comma, Token, Variant, }; pub trait ParseNestedMetaItem { fn get_name(&self) -> &'static str; fn get_found(&self) -> bool; fn get_required(&self) -> bool; fn parse_nested(&mut self, meta: &ParseNestedMeta) -> Option>; } #[derive(Default)] pub struct NestedMetaItem { pub name: &'static str, pub value_required: bool, pub found: bool, pub required: bool, pub value: Option, } impl std::fmt::Debug for NestedMetaItem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("NestedMetaItem") .field("name", &self.name) .field("required", &self.required) .field("value_required", &self.value_required) .field("found", &self.found) .field("value", &self.value.as_ref().map(|v| quote!(#v))) .finish() } } impl NestedMetaItem { pub const fn new(name: &'static str) -> Self { Self { required: false, name, found: false, value_required: false, value: None, } } pub fn required(mut self) -> Self { self.required = true; self } // Note: this flags the `value` as required, that is, // the parameter after the equal: `name = value`. pub const fn value_required(mut self) -> Self { self.value_required = true; self } pub const fn value_optional(mut self) -> Self { self.value_required = false; self } fn parse_nested_forced(&mut self, meta: &ParseNestedMeta) -> syn::Result<()> { if self.value_required || meta.input.peek(Token![=]) { let _eq: Token![=] = meta.input.parse()?; self.value = Some(meta.input.parse()?); } Ok(()) } } impl ParseNestedMetaItem for NestedMetaItem { fn get_name(&self) -> &'static str { self.name } fn parse_nested(&mut self, meta: &ParseNestedMeta) -> Option> { if meta.path.is_ident(self.name) { self.found = true; Some(self.parse_nested_forced(meta)) } else { None } } fn get_found(&self) -> bool { self.found } fn get_required(&self) -> bool { self.required } } pub fn check_meta_items(span: Span, items: &mut [&mut dyn ParseNestedMetaItem]) -> syn::Result<()> { let mut err: Option = None; for item in &mut *items { if item.get_required() && !item.get_found() { let nerr = syn::Error::new( span, format!("attribute `{}` must be specified", item.get_name()), ); if let Some(ref mut err) = err { err.combine(nerr); } else { err = Some(nerr); } } } match err { Some(err) => Err(err), None => Ok(()), } } fn parse_nested_meta_items_from_fn( parse_nested_meta: impl FnOnce( &mut dyn FnMut(ParseNestedMeta) -> syn::Result<()>, ) -> syn::Result<()>, items: &mut [&mut dyn ParseNestedMetaItem], ) -> syn::Result<()> { parse_nested_meta(&mut |meta| { for item in &mut *items { if let Some(res) = item.parse_nested(&meta) { return res; } } Err(meta.error(format!( "unknown attribute `{}`. Possible attributes are {}", meta.path.get_ident().unwrap(), items .iter() .map(|i| format!("`{}`", i.get_name())) .collect::>() .join(", ") ))) })?; Ok(()) } pub fn parse_nested_meta_items_from_stream( input: TokenStream, items: &mut [&mut dyn ParseNestedMetaItem], ) -> syn::Result<()> { parse_nested_meta_items_from_fn( |f| { let p = syn::meta::parser(f); syn::parse::Parser::parse(p, input.into()) }, items, )?; check_meta_items(Span::call_site(), items) } pub fn parse_nested_meta_items<'a>( attrs: impl IntoIterator, attr_name: &str, items: &mut [&mut dyn ParseNestedMetaItem], ) -> syn::Result> { let attr = attrs .into_iter() .find(|attr| attr.path().is_ident(attr_name)); if let Some(attr) = attr { parse_nested_meta_items_from_fn(|x| attr.parse_nested_meta(x), items)?; check_meta_items(attr.span(), items)?; Ok(Some(attr)) } else { Ok(None) } } pub fn parse_optional_nested_meta_items<'a>( attrs: impl IntoIterator, attr_name: &str, items: &mut [&mut dyn ParseNestedMetaItem], ) -> syn::Result> { let attr = attrs .into_iter() .find(|attr| attr.path().is_ident(attr_name)); if let Some(attr) = attr { if let syn::Meta::Path(_) = attr.meta { Ok(Some(attr)) } else { parse_nested_meta_items_from_fn(|x| attr.parse_nested_meta(x), items)?; check_meta_items(attr.span(), items)?; Ok(Some(attr)) } } else { Ok(None) } } pub fn crate_ident_new() -> TokenStream { use proc_macro_crate::FoundCrate; match crate_name("glib") { Ok(FoundCrate::Name(name)) => Some(name), Ok(FoundCrate::Itself) => Some("glib".to_string()), Err(_) => None, } .map(|s| { let glib = Ident::new(&s, Span::call_site()); quote!(#glib) }) .unwrap_or_else(|| { // We couldn't find the glib crate (renamed or not) so let's just hope it's in scope! // // We will be able to have this information once this code is stable: // // ``` // let span = Span::call_site(); // let source = span.source_file(); // let file_path = source.path(); // ``` // // Then we can use proc_macro to parse the file and check if glib is imported somehow. let glib = Ident::new("glib", Span::call_site()); quote!(#glib) }) } // Generate i32 to enum mapping, used to implement // glib::translate::TryFromGlib, such as: // // if value == Animal::Goat as i32 { // return Some(Animal::Goat); // } pub fn gen_enum_from_glib( enum_name: &Ident, enum_variants: &Punctuated, ) -> TokenStream { // FIXME: can we express this with a match()? let recurse = enum_variants.iter().map(|v| { let name = &v.ident; quote_spanned! { v.span() => if value == #enum_name::#name as i32 { return ::core::option::Option::Some(#enum_name::#name); } } }); quote! { #(#recurse)* ::core::option::Option::None } } // These tests are useful to pinpoint the exact location of a macro panic // by running `cargo test --lib` #[cfg(test)] mod tests { use syn::{parse_quote, DeriveInput}; use super::*; fn boxed_stub() -> DeriveInput { parse_quote!( #[boxed_type(name = "Author")] struct Author { name: String, } ) } #[test] fn check_attr_found() { let input = boxed_stub(); let found = parse_nested_meta_items(&input.attrs, "boxed_type", &mut []); matches!(found, Ok(Some(_))); } #[test] fn required_name_present() { let input = boxed_stub(); let mut gtype_name = NestedMetaItem::::new("name") .required() .value_required(); let _ = parse_nested_meta_items(&input.attrs, "boxed_type", &mut [&mut gtype_name]); assert!(gtype_name.get_found()); assert_eq!( gtype_name.value.map(|x| x.value()), Some("Author".to_string()) ); } #[test] fn required_name_none() { let input: DeriveInput = parse_quote!( #[boxed_type(name)] struct Author { name: String, } ); let mut gtype_name = NestedMetaItem::::new("name") .required() .value_required(); let found = parse_nested_meta_items(&input.attrs, "boxed_type", &mut [&mut gtype_name]); // The argument value was specified as required, so an error is returned assert!(found.is_err()); assert!(gtype_name.value.is_none()); // The argument key must be found though assert!(gtype_name.get_found()); } } glib-macros-0.20.4/src/value_delegate_derive.rs000064400000000000000000000145261046102023000175570ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use quote::quote; use syn::{parse::Parse, Token}; use crate::utils::crate_ident_new; #[derive(Default, Debug, Clone)] enum DeriveMode { From, #[default] Private, } pub struct ValueDelegateInput { delegated_ty: syn::Path, ident: syn::Ident, mode: DeriveMode, nullable: bool, } enum Arg { FromPath(syn::Path), Nullable, } impl Parse for Arg { fn parse(input: syn::parse::ParseStream) -> syn::Result { let argname: syn::Ident = input.parse()?; if argname == "nullable" { Ok(Arg::Nullable) } else if argname == "from" { let _eq: Token![=] = input.parse()?; Ok(Arg::FromPath(input.parse()?)) } else { Err(syn::Error::new( input.span(), "expected `nullable` or `from`", )) } } } #[derive(Default)] struct Args { nullable: bool, from_path: Option, } impl Parse for Args { fn parse(input: syn::parse::ParseStream) -> syn::Result { let args = syn::punctuated::Punctuated::::parse_terminated(input)?; let mut this = Args::default(); for a in args { match a { Arg::FromPath(p) => this.from_path = Some(p), Arg::Nullable => this.nullable = true, } } Ok(this) } } impl Parse for ValueDelegateInput { fn parse(input: syn::parse::ParseStream) -> syn::Result { let derive_input: syn::DeriveInput = input.parse()?; let args: Option = if let Some(attr) = derive_input .attrs .iter() .find(|x| x.path().is_ident("value_delegate")) { let args: Args = attr.parse_args()?; Some(args) } else { None }; let (delegated_ty, mode) = if let Some(path) = args.as_ref().and_then(|a| a.from_path.as_ref()) { (Some(path.clone()), DeriveMode::From) } else { let path = match derive_input.data { syn::Data::Struct(s) => match s.fields { syn::Fields::Unnamed(fields) if fields.unnamed.iter().count() == 1 => { fields.unnamed.into_iter().next().and_then(|x| match x.ty { syn::Type::Path(p) => Some(p.path), _ => None, }) } _ => None, }, _ => None, }; (path, DeriveMode::Private) }; let delegated_ty = delegated_ty.ok_or_else(|| { syn::Error::new( derive_input.ident.span(), "Unless `derive(ValueDelegate)` is used over a newtype with 1 field, \ the delegated type must be specified using \ #[value_delegate(from = chosen_type)]", ) })?; Ok(ValueDelegateInput { delegated_ty, ident: derive_input.ident, mode, nullable: args.map(|a| a.nullable).unwrap_or(false), }) } } pub fn impl_value_delegate(input: ValueDelegateInput) -> syn::Result { let ValueDelegateInput { delegated_ty, ident, mode, nullable, .. } = &input; let crate_ident = crate_ident_new(); // this must be called in a context where `this` is defined. let delegate_value = match mode { DeriveMode::From => { quote!(<#delegated_ty as std::convert::From<_>>::from(this)) } DeriveMode::Private => quote!(this.0), }; let to_value_optional = nullable.then(|| { quote! { impl #crate_ident::value::ToValueOptional for #ident { fn to_value_optional(s: ::core::option::Option<&Self>) -> #crate_ident::value::Value { if let ::core::option::Option::Some(this) = s { #crate_ident::value::ToValue::to_value(&::core::option::Option::Some(&#delegate_value)) } else { #crate_ident::value::ToValueOptional::to_value_optional(::core::option::Option::None::<&#delegated_ty>) } } } } }); let from_value = match mode { DeriveMode::From => { quote!(#ident::from(<#delegated_ty as #crate_ident::value::FromValue<'a>>::from_value(value))) } DeriveMode::Private => { quote!(#ident(<#delegated_ty as #crate_ident::value::FromValue<'a>>::from_value(value))) } }; let res = quote! { impl #crate_ident::prelude::StaticType for #ident { fn static_type() -> glib::types::Type { <#delegated_ty as #crate_ident::prelude::StaticType>::static_type() } } impl #crate_ident::value::ToValue for #ident { fn to_value(&self) -> #crate_ident::value::Value { let this = self; #crate_ident::value::ToValue::to_value(&#delegate_value) } fn value_type(&self) -> #crate_ident::types::Type { let this = self; #crate_ident::value::ToValue::value_type(&#delegate_value) } } impl From<#ident> for #crate_ident::value::Value { fn from(this: #ident) -> Self { #crate_ident::value::Value::from(#delegate_value) } } #to_value_optional unsafe impl<'a> #crate_ident::value::FromValue<'a> for #ident { type Checker = <#delegated_ty as #crate_ident::value::FromValue<'a>>::Checker; unsafe fn from_value(value: &'a #crate_ident::value::Value) -> Self { #from_value } } impl #crate_ident::HasParamSpec for #ident { type ParamSpec = <#delegated_ty as #crate_ident::HasParamSpec>::ParamSpec; type SetValue = Self; type BuilderFn = <#delegated_ty as #crate_ident::HasParamSpec>::BuilderFn; fn param_spec_builder() -> Self::BuilderFn { <#delegated_ty as #crate_ident::prelude::HasParamSpec>::param_spec_builder() } } }; Ok(res.into()) } glib-macros-0.20.4/src/variant_derive.rs000064400000000000000000000663131046102023000162560ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use heck::ToKebabCase; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::{Data, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Type}; use crate::utils::crate_ident_new; pub fn impl_variant(input: DeriveInput) -> syn::Result { match input.data { Data::Struct(data_struct) => Ok(derive_variant_for_struct( input.ident, input.generics, data_struct, )), Data::Enum(data_enum) => { let mode = get_enum_mode(&input.attrs)?; let has_data = data_enum .variants .iter() .any(|v| !matches!(v.fields, syn::Fields::Unit)); if has_data { derive_variant_for_enum(input.ident, input.generics, data_enum, mode) } else { Ok(derive_variant_for_c_enum( input.ident, input.generics, data_enum, mode, )) } } Data::Union(..) => Err(syn::Error::new_spanned( input, "#[derive(glib::Variant)] is not available for unions.", )), } } fn derive_variant_for_struct( ident: Ident, generics: Generics, data_struct: syn::DataStruct, ) -> TokenStream { let glib = crate_ident_new(); let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); let (static_variant_type, to_variant, from_variant) = match data_struct.fields { Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { let types = unnamed .into_pairs() .map(|pair| pair.into_value()) .map(|field| field.ty) .collect::>(); let idents = (0..types.len()).map(syn::Index::from).collect::>(); let idents_len = idents.len(); let static_variant_type = quote! { impl #impl_generics #glib::variant::StaticVariantType for #ident #type_generics #where_clause { #[inline] fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> { static TYP: ::std::sync::OnceLock<#glib::VariantType> = ::std::sync::OnceLock::new(); ::std::borrow::Cow::Borrowed(TYP.get_or_init(|| { let mut builder = #glib::GStringBuilder::new("("); #( { let typ = <#types as #glib::variant::StaticVariantType>::static_variant_type(); builder.append(typ.as_str()); } )* builder.append_c(')'); #glib::VariantType::from_string(builder.into_string()).unwrap() })) } } }; let to_variant = quote! { impl #impl_generics #glib::variant::ToVariant for #ident #type_generics #where_clause { fn to_variant(&self) -> #glib::Variant { #glib::Variant::tuple_from_iter(::std::array::IntoIter::<#glib::Variant, #idents_len>::new([ #( #glib::variant::ToVariant::to_variant(&self.#idents) ),* ])) } } impl #impl_generics ::std::convert::From<#ident #type_generics> for #glib::Variant #where_clause { fn from(v: #ident #type_generics) -> #glib::Variant { #glib::Variant::tuple_from_iter(::std::array::IntoIter::<#glib::Variant, #idents_len>::new([ #( <#glib::Variant as ::std::convert::From<_>>::from(v.#idents) ),* ])) } } }; let from_variant = quote! { impl #impl_generics #glib::variant::FromVariant for #ident #type_generics #where_clause { fn from_variant(variant: &#glib::Variant) -> ::core::option::Option { if !variant.is_container() { return ::core::option::Option::None; } ::core::option::Option::Some(Self( #( match variant.try_child_get::<#types>(#idents) { ::core::result::Result::Ok(::core::option::Option::Some(field)) => field, _ => return ::core::option::Option::None, } ),* )) } } }; (static_variant_type, to_variant, from_variant) } Fields::Named(FieldsNamed { named, .. }) => { let fields: Vec<(Ident, Type)> = named .into_pairs() .map(|pair| pair.into_value()) .map(|field| (field.ident.expect("Field ident is specified"), field.ty)) .collect(); let idents: Vec<_> = fields.iter().map(|(ident, _ty)| ident).collect(); let types: Vec<_> = fields.iter().map(|(_ident, ty)| ty).collect(); let counts = (0..types.len()).map(syn::Index::from).collect::>(); let static_variant_type = quote! { impl #impl_generics #glib::variant::StaticVariantType for #ident #type_generics #where_clause { #[inline] fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> { static TYP: ::std::sync::OnceLock<#glib::VariantType> = ::std::sync::OnceLock::new(); ::std::borrow::Cow::Borrowed(TYP.get_or_init(|| unsafe { let ptr = #glib::ffi::g_string_sized_new(16); #glib::ffi::g_string_append_c(ptr, b'(' as _); #( { let typ = <#types as #glib::variant::StaticVariantType>::static_variant_type(); #glib::ffi::g_string_append_len( ptr, typ.as_str().as_ptr() as *const _, typ.as_str().len() as isize, ); } )* #glib::ffi::g_string_append_c(ptr, b')' as _); #glib::translate::from_glib_full( #glib::ffi::g_string_free(ptr, #glib::ffi::GFALSE) as *mut #glib::ffi::GVariantType ) })) } } }; let to_variant = quote! { impl #impl_generics #glib::variant::ToVariant for #ident #type_generics #where_clause { fn to_variant(&self) -> #glib::Variant { #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([ #( #glib::variant::ToVariant::to_variant(&self.#idents) ),* ])) } } impl #impl_generics ::std::convert::From<#ident #type_generics> for #glib::Variant #where_clause { fn from(v: #ident #type_generics) -> #glib::Variant { #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([ #( <#glib::Variant as ::std::convert::From<_>>::from(v.#idents) ),* ])) } } }; let from_variant = quote! { impl #impl_generics #glib::variant::FromVariant for #ident #type_generics #where_clause { fn from_variant(variant: &#glib::Variant) -> ::core::option::Option { if !variant.is_container() { return ::core::option::Option::None; } ::core::option::Option::Some(Self { #( #idents: match variant.try_child_get::<#types>(#counts) { ::core::result::Result::Ok(::core::option::Option::Some(field)) => field, _ => return ::core::option::Option::None, } ),* }) } } }; (static_variant_type, to_variant, from_variant) } Fields::Unit => { let static_variant_type = quote! { impl #impl_generics #glib::variant::StaticVariantType for #ident #type_generics #where_clause { #[inline] fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> { ::std::borrow::Cow::Borrowed(#glib::VariantTy::UNIT) } } }; let to_variant = quote! { impl #impl_generics #glib::variant::ToVariant for #ident #type_generics #where_clause { #[inline] fn to_variant(&self) -> #glib::Variant { #glib::variant::ToVariant::to_variant(&()) } } impl #impl_generics ::std::convert::From<#ident #type_generics> for #glib::Variant #where_clause { #[inline] fn from(v: #ident #type_generics) -> #glib::Variant { #glib::variant::ToVariant::to_variant(&()) } } }; let from_variant = quote! { impl #impl_generics #glib::variant::FromVariant for #ident #type_generics #where_clause { fn from_variant(variant: &#glib::Variant) -> ::core::option::Option { ::core::option::Option::Some(Self) } } }; (static_variant_type, to_variant, from_variant) } }; quote! { #static_variant_type #to_variant #from_variant } } enum EnumMode { String, Repr(Ident), Enum { repr: bool }, Flags { repr: bool }, } impl EnumMode { fn tag_type(&self) -> char { match self { EnumMode::String => 's', EnumMode::Repr(repr) => match repr.to_string().as_str() { "i8" | "i16" => 'n', "i32" => 'i', "i64" => 'x', "u8" => 'y', "u16" => 'q', "u32" => 'u', "u64" => 't', _ => unimplemented!(), }, EnumMode::Enum { repr } => { if *repr { 'i' } else { 's' } } EnumMode::Flags { repr } => { if *repr { 'u' } else { 's' } } } } } fn derive_variant_for_enum( ident: Ident, generics: Generics, data_enum: syn::DataEnum, mode: EnumMode, ) -> syn::Result { let glib = crate_ident_new(); let static_variant_type = format!("({}v)", mode.tag_type()); let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); let to = data_enum.variants.iter().enumerate().map(|(index, v)| { let ident = &v.ident; let tag = match &mode { EnumMode::String => { let nick = ToKebabCase::to_kebab_case(ident.to_string().as_str()); quote! { #nick } }, EnumMode::Repr(repr) => quote! { #index as #repr }, _ => unimplemented!(), }; if !matches!(v.fields, syn::Fields::Unit) { match &mode { EnumMode::Enum { .. } => return Err(syn::Error::new_spanned(v, "#[variant_enum(enum) only allowed with C-style enums using #[derive(glib::Enum)]")), EnumMode::Flags { .. } => return Err(syn::Error::new_spanned(v, "#[variant_enum(flags) only allowed with bitflags using #[glib::flags]")), _ => (), } } Ok(match &v.fields { syn::Fields::Named(FieldsNamed { named, .. }) => { let field_names = named.iter().map(|f| f.ident.as_ref().unwrap()); let field_names2 = field_names.clone(); quote! { Self::#ident { #(#field_names),* } => #glib::variant::ToVariant::to_variant(&( #tag, #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([ #(#glib::variant::ToVariant::to_variant(&#field_names2)),* ])) )) } }, syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { let field_names = unnamed.iter().enumerate().map(|(i, _)| { format_ident!("field{}", i) }); let field_names2 = field_names.clone(); quote! { Self::#ident(#(#field_names),*) => #glib::variant::ToVariant::to_variant(&( #tag, #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([ #(#glib::variant::ToVariant::to_variant(&#field_names2)),* ])) )) } }, syn::Fields::Unit => { quote! { Self::#ident => #glib::variant::ToVariant::to_variant(&( #tag, #glib::variant::ToVariant::to_variant(&()) )) } }, }) }).collect::, _>>()?; let into = data_enum.variants.iter().enumerate().map(|(index, v)| { let field_ident = &v.ident; let tag = match &mode { EnumMode::String => { let nick = ToKebabCase::to_kebab_case(field_ident.to_string().as_str()); quote! { #nick } } EnumMode::Repr(repr) => quote! { #index as #repr }, _ => unimplemented!(), }; match &v.fields { syn::Fields::Named(FieldsNamed { named, .. }) => { let field_names = named.iter().map(|f| f.ident.as_ref().unwrap()); let field_names2 = field_names.clone(); quote! { #ident::#field_ident { #(#field_names),* } => #glib::variant::ToVariant::to_variant(&( #tag, #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([ #(<#glib::Variant as ::std::convert::From<_>>::from(#field_names2)),* ])) )) } } syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { let field_names = unnamed .iter() .enumerate() .map(|(i, _)| format_ident!("field{}", i)); let field_names2 = field_names.clone(); quote! { #ident::#field_ident(#(#field_names),*) => #glib::variant::ToVariant::to_variant(&( #tag, #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([ #(<#glib::Variant as ::std::convert::From<_>>::from(#field_names2)),* ])) )) } } syn::Fields::Unit => { quote! { #ident::#field_ident => #glib::variant::ToVariant::to_variant(&( #tag, #glib::variant::ToVariant::to_variant(&()) )) } } } }); let from = data_enum.variants.iter().enumerate().map(|(index, v)| { let ident = &v.ident; let tag = match &mode { EnumMode::String => { let nick = ToKebabCase::to_kebab_case(ident.to_string().as_str()); quote! { #nick } } EnumMode::Repr(_) => quote! { #index }, _ => unimplemented!(), }; match &v.fields { syn::Fields::Named(FieldsNamed { named, .. }) => { let fields = named.iter().enumerate().map(|(index, f)| { let name = f.ident.as_ref().unwrap(); let repr = &f.ty; quote! { #name: <#repr as #glib::variant::FromVariant>::from_variant( &#glib::Variant::try_child_value(&value, #index)? )? } }); quote! { #tag => ::core::option::Option::Some(Self::#ident { #(#fields),* }), } } syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { let indices = 0..unnamed.iter().count(); let repr = unnamed.iter().map(|f| &f.ty); quote! { #tag => ::core::option::Option::Some(Self::#ident( #( <#repr as #glib::variant::FromVariant>::from_variant( &#glib::Variant::try_child_value(&value, #indices)? )? ),* )), } } syn::Fields::Unit => { quote! { #tag => ::core::option::Option::Some(Self::#ident), } } } }); let (repr, tag_match) = match &mode { EnumMode::String => (quote! { String }, quote! { tag.as_str() }), EnumMode::Repr(repr) => (quote! { #repr }, quote! { tag as usize }), _ => unimplemented!(), }; Ok(quote! { impl #impl_generics #glib::variant::StaticVariantType for #ident #type_generics #where_clause { #[inline] fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> { ::std::borrow::Cow::Borrowed( unsafe { #glib::VariantTy::from_str_unchecked(#static_variant_type) } ) } } impl #impl_generics #glib::variant::ToVariant for #ident #type_generics #where_clause { fn to_variant(&self) -> #glib::Variant { match self { #(#to),* } } } impl #impl_generics ::std::convert::From<#ident #type_generics> for #glib::Variant #where_clause { fn from(v: #ident #type_generics) -> #glib::Variant { match v { #(#into),* } } } impl #impl_generics #glib::variant::FromVariant for #ident #type_generics #where_clause { fn from_variant(variant: &#glib::Variant) -> ::core::option::Option { let (tag, value) = <(#repr, #glib::Variant) as #glib::variant::FromVariant>::from_variant(&variant)?; if !#glib::VariantTy::is_tuple(#glib::Variant::type_(&value)) { return ::core::option::Option::None; } match #tag_match { #(#from)* _ => ::core::option::Option::None } } } }) } fn derive_variant_for_c_enum( ident: Ident, generics: Generics, data_enum: syn::DataEnum, mode: EnumMode, ) -> TokenStream { let glib = crate_ident_new(); let static_variant_type = mode.tag_type().to_string(); let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); let (to_variant, from_variant) = match mode { EnumMode::String => { let idents = data_enum.variants.iter().map(|v| &v.ident); let nicks = data_enum.variants.iter().map(|v| { let nick = ToKebabCase::to_kebab_case(v.ident.to_string().as_str()); quote! { #nick } }); let idents2 = idents.clone(); let nicks2 = nicks.clone(); ( quote! { #glib::variant::ToVariant::to_variant(match self { #(Self::#idents => #nicks),* }) }, quote! { let tag = #glib::Variant::str(&variant)?; match tag { #(#nicks2 => ::core::option::Option::Some(Self::#idents2),)* _ => ::core::option::Option::None } }, ) } EnumMode::Repr(repr) => { let idents = data_enum.variants.iter().map(|v| &v.ident); ( quote! { #glib::variant::ToVariant::to_variant(&(*self as #repr)) }, quote! { let value = <#repr as #glib::variant::FromVariant>::from_variant(&variant)?; #(if value == Self::#idents as #repr { return ::core::option::Option::Some(Self::#idents); })* ::core::option::Option::None }, ) } EnumMode::Enum { repr: true } => ( quote! { #glib::variant::ToVariant::to_variant(&(*self as i32)) }, quote! { let value = ::from_variant(&variant)?; unsafe { #glib::translate::try_from_glib(value) }.ok() }, ), EnumMode::Enum { repr: false } => ( quote! { let enum_class = #glib::EnumClass::new::(); let value = ::into_glib(*self); let value = #glib::EnumClass::value(&enum_class, value); let value = ::core::option::Option::unwrap(value); let nick = #glib::EnumValue::nick(&value); #glib::variant::ToVariant::to_variant(nick) }, quote! { let enum_class = #glib::EnumClass::new::(); let tag = #glib::Variant::str(&variant)?; let value = #glib::EnumClass::value_by_nick(&enum_class, tag)?; let value = #glib::EnumValue::value(&value); unsafe { #glib::translate::try_from_glib(value) }.ok() }, ), EnumMode::Flags { repr: true } => ( quote! { #glib::variant::ToVariant::to_variant(&self.bits()) }, quote! { let value = ::from_variant(&variant)?; Self::from_bits(value) }, ), EnumMode::Flags { repr: false } => ( quote! { let flags_class = #glib::FlagsClass::new::(); let value = ::into_glib(*self); let s = #glib::FlagsClass::to_nick_string(&flags_class, value); #glib::variant::ToVariant::to_variant(&s) }, quote! { let flags_class = #glib::FlagsClass::new::(); let s = #glib::Variant::str(&variant)?; let value = #glib::FlagsClass::from_nick_string(&flags_class, s).ok()?; ::core::option::Option::Some(unsafe { #glib::translate::from_glib(value) }) }, ), }; quote! { impl #impl_generics #glib::variant::StaticVariantType for #ident #type_generics #where_clause { #[inline] fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> { ::std::borrow::Cow::Borrowed( unsafe { #glib::VariantTy::from_str_unchecked(#static_variant_type) } ) } } impl #impl_generics #glib::variant::ToVariant for #ident #type_generics #where_clause { fn to_variant(&self) -> #glib::Variant { #to_variant } } impl #impl_generics ::std::convert::From<#ident #type_generics> for #glib::Variant #where_clause { #[inline] fn from(v: #ident #type_generics) -> #glib::Variant { <#ident #type_generics as #glib::variant::ToVariant>::to_variant(&v) } } impl #impl_generics #glib::variant::FromVariant for #ident #type_generics #where_clause { fn from_variant(variant: &#glib::Variant) -> ::core::option::Option { #from_variant } } } } fn get_enum_mode(attrs: &[syn::Attribute]) -> syn::Result { let attr = attrs.iter().find(|a| a.path().is_ident("variant_enum")); let Some(attr) = attr else { return Ok(EnumMode::String); }; let mut repr_attr = None; let mut mode = EnumMode::String; attr.parse_nested_meta(|meta| { match meta.path.get_ident().map(|id| id.to_string()).as_deref() { Some("repr") => { repr_attr = Some(meta.path); Ok(()) } Some("enum") => { mode = EnumMode::Enum { repr: false }; Ok(()) } Some("flags") => { mode = EnumMode::Flags { repr: false }; Ok(()) } _ => Err(syn::Error::new_spanned( meta.path, "unknown type in #[variant_enum] attribute", )), } })?; Ok(match mode { EnumMode::String if repr_attr.is_some() => { let repr_attr = repr_attr.unwrap(); let repr = get_repr(attrs).ok_or_else(|| { syn::Error::new_spanned( repr_attr, "Must have #[repr] attribute with one of i8, i16, i32, i64, u8, u16, u32, u64", ) })?; EnumMode::Repr(repr) } EnumMode::Enum { .. } => EnumMode::Enum { repr: repr_attr.is_some(), }, EnumMode::Flags { .. } => EnumMode::Flags { repr: repr_attr.is_some(), }, e => e, }) } fn get_repr(attrs: &[syn::Attribute]) -> Option { let attr = attrs.iter().find(|a| a.path().is_ident("repr"))?; let mut repr_ty = None; attr.parse_nested_meta(|meta| { repr_ty = Some(meta.path.get_ident().unwrap().clone()); Ok(()) }) .unwrap(); match repr_ty.as_ref()?.to_string().as_str() { "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64" => Some(repr_ty?), _ => None, } } glib-macros-0.20.4/tests/clone.rs000064400000000000000000000271101046102023000147170ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use std::rc::Rc; use glib::clone; #[test] fn clone() { let _ = clone!(move || {}); let fut = clone!(async move {}); drop(fut); let x = 1; let _ = clone!(move || { println!("foo {x}"); 1 }); let x = 1; let y = String::from("123"); let v = Rc::new(1); let _ = clone!( #[strong] v, move || { println!("foo {x} {y} {v}"); 1 } ); let v = Rc::new(1); let _ = clone!( #[strong(rename_to = y)] v, move || { println!("foo {y}"); 1 } ); let v = Rc::new(1); let _ = clone!( #[strong(rename_to = y)] Rc::strong_count(&v), move || { println!("foo {y}"); 1 } ); let v = Rc::new(1); let _ = clone!( #[strong] v, move |a: i32, b: &str| { println!("foo {a} {b} {v}"); 1 } ); let x = 1; let y = String::from("123"); let v = Rc::new(1); let fut = clone!( #[strong] v, async move { println!("foo {x} {y} {v}"); 1 } ); drop(fut); let v = Rc::new(1); let _ = clone!( #[weak] v, move || { println!("foo {v}"); } ); let v = Rc::new(1); let _ = clone!( #[weak] v, #[upgrade_or_else] || None::, move || { println!("foo {v}"); Some(1) } ); let v = Rc::new(1); let _ = clone!( #[weak] v, #[upgrade_or] None::, move || { println!("foo {v}"); Some(1) } ); let v = Rc::new(1); let _ = clone!( #[weak] v, #[upgrade_or_default] move || { println!("foo {v}"); Some(1) } ); let v = Rc::new(1); let w = Rc::new(2); let x = Rc::new(3); let _ = clone!( #[weak] v, #[weak] w, #[upgrade_or_else] || { let x: Rc = x; Some(*x) }, move || { println!("foo {v} {w}"); Some(1) } ); let v = Rc::new(1); let _ = clone!( #[weak] v, #[upgrade_or_panic] move || { println!("foo {v}"); Some(1) } ); let v = Rc::new(1); let _ = clone!( #[weak_allow_none] v, move || { println!("foo {}", v.unwrap()); Some(1) } ); let v = "123"; let _ = clone!( #[to_owned] v, move || { println!("foo {v}"); 1 } ); } const TESTS: &[(&str, &str)] = &[ ("clone!()", "expected a closure or async block"), ( "clone!(#[weak] a, #[weak] b, |x| {})", r#"error: closures need to capture variables by move. Please add the `move` keyword --> test_1.rs:1:88 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[weak] a, #[weak] b, |x| {}); } | ^^^^^^"#, ), ( "clone!(#[strong] self, move |x| {})", r#"error: capture attribute for `self` requires usage of the `rename_to` attribute property --> test_2.rs:1:66 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[strong] self, move |x| {}); } | ^^^^^^^^^"#, ), ( "clone!(#[strong] self.v, move |x| {})", r#"error: capture attribute for an expression requires usage of the `rename_to` attribute property --> test_3.rs:1:66 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[strong] self.v, move |x| {}); } | ^^^^^^^^^"#, ), ( "clone!(#[strong(rename_to = x, rename_to = y)] self.v, move || {})", r#"error: multiple `rename_to` properties are not allowed --> test_4.rs:1:90 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[strong(rename_to = x, rename_to = y)] self.v, move || {}); } | ^^^^^^^^^^^^^"#, ), ( "clone!(#[strong(stronk)] self.v, move || {})", r#"error: unsupported capture attribute property `stronk`: only `rename_to` is supported --> test_5.rs:1:75 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[strong(stronk)] self.v, move || {}); } | ^^^^^^"#, ), ( "clone!(#[strong(rename_to = \"a\")] self.v, move || {})", r#"error: expected identifier --> test_6.rs:1:87 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[strong(rename_to = "a")] self.v, move || {}); } | ^^^"#, ), ( "clone!(#[weak] v, #[upgrade_or_else] false, move || {})", r#"error: expected `|` --> test_7.rs:1:96 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[weak] v, #[upgrade_or_else] false, move || {}); } | ^^^^^"#, ), ( "clone!(#[weak] v, #[upgrade_or(abort)] move || {})", r#"error: unexpected token in attribute --> test_8.rs:1:89 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[weak] v, #[upgrade_or(abort)] move || {}); } | ^"#, ), ( "clone!(#[yolo] v, move || {})", r#"error: unsupported attribute `yolo`: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported --> test_9.rs:1:66 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[yolo] v, move || {}); } | ^^^^^^^"#, ), ( "clone!(#[watch] v, move || {})", r#"error: watch variable captures are not supported --> test_10.rs:1:66 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[watch] v, move || {}); } | ^^^^^^^^"#, ), ( "clone!(#[strong]#[strong] v, move || {})", r#"error: variable capture attributes must be followed by an identifier --> test_11.rs:1:75 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[strong]#[strong] v, move || {}); } | ^^^^^^^^^"#, ), ( "clone!(v, move || {})", r#"error: only closures and async blocks are supported --> test_12.rs:1:66 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(v, move || {}); } | ^"#, ), ( "clone!(#[upgrade_or_else] || lol, #[strong] v, move || {println!(\"foo\");})", r#"error: upgrade failure attribute must not be followed by any other attributes. Found 1 more attribute --> test_13.rs:1:93 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[upgrade_or_else] || lol, #[strong] v, move || {println!("foo");}); } | ^^^^^^^^^"#, ), ( "clone!(#[upgrade_or_else] |x| lol, #[strong] v, move || {println!(\"foo\");})", r#"error: `upgrade_or_else` closure must not have any parameters --> test_14.rs:1:85 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[upgrade_or_else] |x| lol, #[strong] v, move || {println!("foo");}); } | ^^^^^^^"#, ), ( "clone!(#[upgrade_or_else] async || lol, #[strong] v, move || {println!(\"foo\");})", r#"error: `upgrade_or_else` closure needs to be a non-async closure --> test_15.rs:1:85 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[upgrade_or_else] async || lol, #[strong] v, move || {println!("foo");}... | ^^^^^^^^^^^^"#, ), ( "clone!(#[upgrade_or_panic] #[strong] v, move || {println!(\"foo\");})", r#"error: upgrade failure attribute must not be followed by any other attributes. Found 1 more attribute --> test_16.rs:1:86 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[upgrade_or_panic] #[strong] v, move || {println!("foo");}); } | ^^^^^^^^^"#, ), ( "clone!(#[strong] v, #[upgrade_or_panic] move || {println!(\"foo\");})", r#"error: upgrade failure attribute can only be used together with weak variable captures --> test_17.rs:1:79 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[strong] v, #[upgrade_or_panic] move || {println!("foo");}); } | ^"#, ), // The async part! ( "clone!(#[strong] v, async {println!(\"foo\");})", r#"error: async blocks need to capture variables by move. Please add the `move` keyword --> test_18.rs:1:79 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[strong] v, async {println!("foo");}); } | ^^^^^^^^^^^^^^^^^^^^^^^^"#, ), ( "clone!(#[strong] v, {println!(\"foo\");})", r#"error: only closures and async blocks are supported --> test_19.rs:1:79 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(#[strong] v, {println!("foo");}); } | ^^^^^^^^^^^^^^^^^^"#, ), ]; #[test] fn clone_failures() { let t = trybuild2::TestCases::new(); for (index, (expr, err)) in TESTS.iter().enumerate() { let prefix = "fn main() { use glib::clone; let v = std::rc::Rc::new(1); "; let suffix = "; }"; let output = format!("{prefix}{expr}{suffix}"); t.compile_fail_inline_check_sub(&format!("test_{index}"), &output, err); } } const NO_WARNING: &[&str] = &[ "let _ = clone!(#[weak] v, #[upgrade_or] (), move || println!(\"{}\", v))", "let _ = clone!(#[weak] v, #[upgrade_or_else] || (), move || println!(\"{}\", v))", "let _ = clone!(#[weak] v, #[upgrade_or_else] || (()), move || println!(\"{}\", v))", "let _ = clone!(#[weak] v, #[upgrade_or_else] || ( () ), move || println!(\"{}\", v))", "let _ = clone!(#[weak] v, #[upgrade_or_else] || ( ), move || println!(\"{}\", v))", ]; // Ensures that no warning are emitted if the return value is a unit tuple. #[test] fn clone_unit_tuple_return() { let t = trybuild2::TestCases::new(); for (index, expr) in NO_WARNING.iter().enumerate() { let prefix = "fn main() { use glib::clone; let v = std::rc::Rc::new(1); "; let suffix = "; }"; let output = format!("{prefix}{expr}{suffix}"); t.pass_inline(&format!("test_{index}"), &output); } } glib-macros-0.20.4/tests/closure.rs000064400000000000000000000177111046102023000153010ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. const TESTS: &[(&str, &str)] = &[ ("closure!()", "expected a closure"), ( "closure!(#[weak] a, #[weak] b, |x| {})", r#"error: closures need to capture variables by move. Please add the `move` keyword --> test_1.rs:1:92 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[weak] a, #[weak] b, |x| {}); } | ^^^^^^"#, ), ( "closure!(#[strong] self, move |x| {})", r#"error: capture attribute for `self` requires usage of the `rename_to` attribute property --> test_2.rs:1:70 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[strong] self, move |x| {}); } | ^^^^^^^^^"#, ), ( "closure!(#[strong] self.v, move |x| {})", r#"error: capture attribute for an expression requires usage of the `rename_to` attribute property --> test_3.rs:1:70 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[strong] self.v, move |x| {}); } | ^^^^^^^^^"#, ), ( "closure!(#[strong(rename_to = x, rename_to = y)] self.v, move || {})", r#"error: multiple `rename_to` properties are not allowed --> test_4.rs:1:94 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[strong(rename_to = x, rename_to = y)] self.v, move || {}); } | ^^^^^^^^^^^^^"#, ), ( "closure!(#[strong(stronk)] self.v, move || {})", r#"error: unsupported capture attribute property `stronk`: only `rename_to` is supported --> test_5.rs:1:79 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[strong(stronk)] self.v, move || {}); } | ^^^^^^"#, ), ( "closure!(#[strong(rename_to = \"a\")] self.v, move || {})", r#"error: expected identifier --> test_6.rs:1:91 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[strong(rename_to = "a")] self.v, move || {}); } | ^^^"#, ), ( "closure!(#[weak] v, #[upgrade_or_else] false, move || {})", r#"error: expected `|` --> test_7.rs:1:100 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[weak] v, #[upgrade_or_else] false, move || {}); } | ^^^^^"#, ), ( "closure!(#[weak] v, #[upgrade_or(abort)] move || {})", r#"error: unexpected token in attribute --> test_8.rs:1:93 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[weak] v, #[upgrade_or(abort)] move || {}); } | ^"#, ), ( "closure!(#[yolo] v, move || {})", r#"error: unsupported attribute `yolo`: only `watch`, `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported --> test_9.rs:1:70 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[yolo] v, move || {}); } | ^^^^^^^"#, ), ( "closure!(#[watch] v, #[watch] v, move || {})", r#"error: only one `watch` capture is allowed per closure --> test_10.rs:1:82 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[watch] v, #[watch] v, move || {}); } | ^^^^^^^^"#, ), ( "closure!(#[strong]#[strong] v, move || {})", r#"error: variable capture attributes must be followed by an identifier --> test_11.rs:1:79 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[strong]#[strong] v, move || {}); } | ^^^^^^^^^"#, ), ( "closure!(v, move || {})", r#"error: expected `|` --> test_12.rs:1:70 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(v, move || {}); } | ^"#, ), ( "closure!(#[upgrade_or_else] || lol, #[strong] v, move || {println!(\"foo\");})", r#"error: upgrade failure attribute must not be followed by any other attributes. Found 1 more attribute --> test_13.rs:1:97 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[upgrade_or_else] || lol, #[strong] v, move || {println!("foo");}); } | ^^^^^^^^^"#, ), ( "closure!(#[upgrade_or_else] |x| lol, #[strong] v, move || {println!(\"foo\");})", r#"error: `upgrade_or_else` closure must not have any parameters --> test_14.rs:1:89 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[upgrade_or_else] |x| lol, #[strong] v, move || {println!("foo");}); } | ^^^^^^^"#, ), ( "closure!(#[upgrade_or_else] async || lol, #[strong] v, move || {println!(\"foo\");})", r#"error: `upgrade_or_else` closure needs to be a non-async closure --> test_15.rs:1:89 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[upgrade_or_else] async || lol, #[strong] v, move || {println!("foo... | ^^^^^^^^^^^^"#, ), ( "closure!(#[upgrade_or_panic] #[strong] v, move || {println!(\"foo\");})", r#"error: upgrade failure attribute must not be followed by any other attributes. Found 1 more attribute --> test_16.rs:1:90 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[upgrade_or_panic] #[strong] v, move || {println!("foo");}); } | ^^^^^^^^^"#, ), ( "closure!(#[strong] v, async {println!(\"foo\");})", r#"error: expected `|` --> test_17.rs:1:89 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[strong] v, async {println!("foo");}); } | ^"#, ), ( "closure!(#[strong] v, {println!(\"foo\");})", r#"error: expected `|` --> test_18.rs:1:83 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[strong] v, {println!("foo");}); } | ^"#, ), ( "closure!(#[strong] v, #[upgrade_or_panic] move || {println!(\"foo\");})", r#"error: upgrade failure attribute can only be used together with weak variable captures --> test_19.rs:1:83 | 1 | fn main() { use glib::closure; let v = std::rc::Rc::new(1); closure!(#[strong] v, #[upgrade_or_panic] move || {println!("foo");}); } | ^"#, ), ]; #[test] fn closure_failures() { let t = trybuild2::TestCases::new(); for (index, (expr, err)) in TESTS.iter().enumerate() { let prefix = "fn main() { use glib::closure; let v = std::rc::Rc::new(1); "; let suffix = "; }"; let output = format!("{prefix}{expr}{suffix}"); t.compile_fail_inline_check_sub(&format!("test_{index}"), &output, err); } } glib-macros-0.20.4/tests/enum_dynamic.rs000064400000000000000000000451101046102023000162670ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use glib::{prelude::*, subclass::prelude::*}; mod module { use super::*; mod imp { use super::*; // impl for a type module (must extend `glib::TypeModule` and must implement `glib::TypePlugin`). #[derive(Default)] pub struct MyModule; #[glib::object_subclass] impl ObjectSubclass for MyModule { const NAME: &'static str = "MyModule"; type Type = super::MyModule; type ParentType = glib::TypeModule; type Interfaces = (glib::TypePlugin,); } impl ObjectImpl for MyModule {} impl TypePluginImpl for MyModule {} impl TypeModuleImpl for MyModule { fn load(&self) -> bool { // registers enums as dynamic types. let my_module = self.obj(); let type_module: &glib::TypeModule = my_module.upcast_ref(); super::MyModuleEnum::on_implementation_load(type_module) && super::MyModuleEnumLazy::on_implementation_load(type_module) } fn unload(&self) { // marks the enums as unregistered. let my_module = self.obj(); let type_module: &glib::TypeModule = my_module.upcast_ref(); super::MyModuleEnumLazy::on_implementation_unload(type_module); super::MyModuleEnum::on_implementation_unload(type_module); } } } // an enum to register as a dynamic type. #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::Enum)] #[repr(u32)] #[enum_type(name = "MyModuleEnum")] #[enum_dynamic] pub enum MyModuleEnum { #[enum_value(name = "Foo")] Foo, Bar, } // an enum to lazy register as a dynamic type. #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::Enum)] #[repr(u32)] #[enum_type(name = "MyModuleEnumLazy")] #[enum_dynamic(lazy_registration = true)] pub enum MyModuleEnumLazy { #[enum_value(name = "Foo")] Foo, Bar, } // a module (must extend `glib::TypeModule` and must implement `glib::TypePlugin`). glib::wrapper! { pub struct MyModule(ObjectSubclass) @extends glib::TypeModule, @implements glib::TypePlugin; } #[test] fn dynamic_enums() { // 1st: creates a single module to test with. let module = glib::Object::new::(); // 1st: uses it to test lifecycle of enums registered as dynamic types. dynamic_enums_lifecycle(&module); // 2nd: uses it to test behavior of enums registered as dynamic types. dynamic_enums_behavior(&module); } // tests lifecycle of enums registered as dynamic types within a module. fn dynamic_enums_lifecycle(module: &MyModule) { // checks types of enums to register as dynamic types are invalid (module is not loaded yet). assert!(!MyModuleEnum::static_type().is_valid()); assert!(!MyModuleEnumLazy::static_type().is_valid()); // simulates the GLib type system to load/unload the module. TypeModuleExt::use_(module); TypeModuleExt::unuse(module); // checks types of enums registered as dynamic types are valid (module is unloaded). assert!(MyModuleEnum::static_type().is_valid()); // checks types of enums that are lazy registered as dynamic types are valid (module is unloaded). assert!(!MyModuleEnumLazy::static_type().is_valid()); // simulates the GLib type system to load the module. TypeModuleExt::use_(module); // checks types of enums registered as dynamic types are valid (module is loaded). let enum_type = MyModuleEnum::static_type(); assert!(enum_type.is_valid()); let enum_lazy_type = MyModuleEnumLazy::static_type(); assert!(enum_lazy_type.is_valid()); // checks plugin of enums registered as dynamic types is `MyModule`. assert_eq!( enum_type.plugin().as_ref(), Some(module.upcast_ref::()) ); assert_eq!( enum_lazy_type.plugin().as_ref(), Some(module.upcast_ref::()) ); // simulates the GLib type system to unload the module. TypeModuleExt::unuse(module); // checks types of enums registered as dynamic types are still valid (should have been marked as unloaded by the GLib type system but this cannot be checked). assert!(MyModuleEnum::static_type().is_valid()); assert!(MyModuleEnumLazy::static_type().is_valid()); // simulates the GLib type system to reload the module. TypeModuleExt::use_(module); // checks types of enums registered as dynamic types are still valid (should have been marked as loaded by the GLib type system but this cannot be checked). assert!(MyModuleEnum::static_type().is_valid()); assert!(MyModuleEnumLazy::static_type().is_valid()); // simulates the GLib type system to unload the module. TypeModuleExt::unuse(module); } // tests behavior of enums registered as dynamic types within a module. fn dynamic_enums_behavior(module: &MyModule) { use glib::prelude::*; use glib::translate::{FromGlib, IntoGlib}; // simulates the GLib type system to load the module. TypeModuleExt::use_(module); assert_eq!(MyModuleEnum::Foo.into_glib(), 0); assert_eq!(MyModuleEnum::Bar.into_glib(), 1); assert_eq!(unsafe { MyModuleEnum::from_glib(0) }, MyModuleEnum::Foo); assert_eq!(unsafe { MyModuleEnum::from_glib(1) }, MyModuleEnum::Bar); let t = MyModuleEnum::static_type(); assert!(t.is_a(glib::Type::ENUM)); assert_eq!(t.name(), "MyModuleEnum"); let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); let values = e.values(); assert_eq!(values.len(), 2); assert_eq!(values[0].name(), "Foo"); assert_eq!(values[0].nick(), "foo"); assert_eq!(values[1].name(), "Bar"); assert_eq!(values[1].nick(), "bar"); let v = e.value(0).expect("EnumClass::get_value(0) failed"); assert_eq!(v.name(), "Foo"); assert_eq!(v.nick(), "foo"); let v = e.value(1).expect("EnumClass::get_value(1) failed"); assert_eq!(v.name(), "Bar"); assert_eq!(v.nick(), "bar"); assert_eq!(e.value(2), None); // within enums registered as dynamic types, values are usables only if // at least one type class ref exists (see `glib::EnumClass`). assert_eq!( MyModuleEnum::Foo.to_value().get::(), Ok(MyModuleEnum::Foo) ); assert_eq!( MyModuleEnum::Bar.to_value().get::(), Ok(MyModuleEnum::Bar) ); assert_eq!(MyModuleEnumLazy::Foo.into_glib(), 0); assert_eq!(MyModuleEnumLazy::Bar.into_glib(), 1); assert_eq!( unsafe { MyModuleEnumLazy::from_glib(0) }, MyModuleEnumLazy::Foo ); assert_eq!( unsafe { MyModuleEnumLazy::from_glib(1) }, MyModuleEnumLazy::Bar ); let t = MyModuleEnumLazy::static_type(); assert!(t.is_a(glib::Type::ENUM)); assert_eq!(t.name(), "MyModuleEnumLazy"); let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); let values = e.values(); assert_eq!(values.len(), 2); assert_eq!(values[0].name(), "Foo"); assert_eq!(values[0].nick(), "foo"); assert_eq!(values[1].name(), "Bar"); assert_eq!(values[1].nick(), "bar"); let v = e.value(0).expect("EnumClass::get_value(0) failed"); assert_eq!(v.name(), "Foo"); assert_eq!(v.nick(), "foo"); let v = e.value(1).expect("EnumClass::get_value(1) failed"); assert_eq!(v.name(), "Bar"); assert_eq!(v.nick(), "bar"); assert_eq!(e.value(2), None); // within enums registered as dynamic types, values are usables only if // at least one type class ref exists (see `glib::EnumClass`). assert_eq!( MyModuleEnumLazy::Foo.to_value().get::(), Ok(MyModuleEnumLazy::Foo) ); assert_eq!( MyModuleEnumLazy::Bar.to_value().get::(), Ok(MyModuleEnumLazy::Bar) ); // simulates the GLib type system to unload the module. TypeModuleExt::unuse(module); } } pub mod plugin { use super::*; pub mod imp { use glib::EnumClass; use super::*; use std::cell::Cell; // impl for a type plugin (must implement `glib::TypePlugin`). #[derive(Default)] pub struct MyPlugin { my_enum_type_values: Cell>, my_enum_lazy_type_values: Cell>, } #[glib::object_subclass] impl ObjectSubclass for MyPlugin { const NAME: &'static str = "MyPlugin"; type Type = super::MyPlugin; type Interfaces = (glib::TypePlugin,); } impl ObjectImpl for MyPlugin {} impl TypePluginImpl for MyPlugin { fn use_plugin(&self) { // register enums as dynamic types. let my_plugin = self.obj(); super::MyPluginEnum::on_implementation_load(my_plugin.as_ref()); super::MyPluginEnumLazy::on_implementation_load(my_plugin.as_ref()); } fn unuse_plugin(&self) { // marks enums as unregistered. let my_plugin = self.obj(); super::MyPluginEnumLazy::on_implementation_unload(my_plugin.as_ref()); super::MyPluginEnum::on_implementation_unload(my_plugin.as_ref()); } fn complete_type_info( &self, type_: glib::Type, ) -> (glib::TypeInfo, glib::TypeValueTable) { let enum_type_values = match type_ { type_ if type_ == super::MyPluginEnum::static_type() => { self.my_enum_type_values.get() } type_ if type_ == super::MyPluginEnumLazy::static_type() => { self.my_enum_lazy_type_values.get() } _ => panic!("unexpected type"), } .expect("enum type values"); let type_info = EnumClass::complete_type_info(type_, enum_type_values) .expect("EnumClass::complete_type_info failed"); (type_info, glib::TypeValueTable::default()) } } impl TypePluginRegisterImpl for MyPlugin { fn register_dynamic_enum( &self, type_name: &str, const_static_values: &'static glib::enums::EnumValues, ) -> glib::Type { let type_ = glib::Type::from_name(type_name).unwrap_or_else(|| { glib::Type::register_dynamic( glib::Type::ENUM, type_name, self.obj().upcast_ref::(), glib::TypeFlags::NONE, ) }); if type_.is_valid() { match type_name { "MyPluginEnum" => self.my_enum_type_values.set(Some(const_static_values)), "MyPluginEnumLazy" => { self.my_enum_lazy_type_values.set(Some(const_static_values)) } _ => panic!("unexpected"), }; } type_ } } } // an enum to register as a dynamic type. #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::Enum)] #[repr(u32)] #[enum_type(name = "MyPluginEnum")] #[enum_dynamic(plugin_type = MyPlugin)] pub enum MyPluginEnum { #[enum_value(name = "Foo")] Foo, Bar, } // an enum to lazy register as a dynamic type. #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::Enum)] #[repr(u32)] #[enum_type(name = "MyPluginEnumLazy")] #[enum_dynamic(plugin_type = MyPlugin, lazy_registration = true)] pub enum MyPluginEnumLazy { #[enum_value(name = "Foo")] Foo, Bar, } // a plugin (must implement `glib::TypePlugin`). glib::wrapper! { pub struct MyPlugin(ObjectSubclass) @implements glib::TypePlugin; } #[test] fn dynamic_enums() { // 1st: creates a single plugin to test with. let plugin = glib::Object::new::(); // 1st: uses it to test lifecycle of enums registered as dynamic types. dynamic_enums_lifecycle(&plugin); // 2nd: uses it to test behavior of enums registered as dynamic types. dynamic_enums_behavior(&plugin); } // tests lifecycle of enums registered as dynamic types within a plugin. fn dynamic_enums_lifecycle(plugin: &MyPlugin) { use glib::prelude::*; // checks types of enums to register as dynamic types are invalid (plugin is not used yet). assert!(!MyPluginEnum::static_type().is_valid()); assert!(!MyPluginEnumLazy::static_type().is_valid()); // simulates the GLib type system to use/unuse the plugin. TypePluginExt::use_(plugin); TypePluginExt::unuse(plugin); // checks types of enums registered as dynamic types are valid (plugin is unused). assert!(MyPluginEnum::static_type().is_valid()); // checks types of enums that are lazy registered as dynamic types are still invalid (plugin is unused). assert!(!MyPluginEnumLazy::static_type().is_valid()); // simulates the GLib type system to use the plugin. TypePluginExt::use_(plugin); // checks types of enums registered as dynamic types are valid (plugin is used). let enum_type = MyPluginEnum::static_type(); assert!(enum_type.is_valid()); let enum_lazy_type = MyPluginEnumLazy::static_type(); assert!(enum_lazy_type.is_valid()); // checks plugin of enums registered as dynamic types is `MyPlugin`. assert_eq!( enum_type.plugin().as_ref(), Some(plugin.upcast_ref::()) ); assert_eq!( enum_lazy_type.plugin().as_ref(), Some(plugin.upcast_ref::()) ); // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(plugin); // checks types of enums registered as dynamic types are still valid. assert!(MyPluginEnum::static_type().is_valid()); assert!(MyPluginEnumLazy::static_type().is_valid()); // simulates the GLib type system to reuse the plugin. TypePluginExt::use_(plugin); // checks types of enums registered as dynamic types are still valid. assert!(MyPluginEnum::static_type().is_valid()); assert!(MyPluginEnumLazy::static_type().is_valid()); // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(plugin); } // tests behavior of enums registered as dynamic types within a plugin. fn dynamic_enums_behavior(plugin: &MyPlugin) { use glib::prelude::*; use glib::translate::{FromGlib, IntoGlib}; // simulates the GLib type system to use the plugin. TypePluginExt::use_(plugin); assert_eq!(MyPluginEnum::Foo.into_glib(), 0); assert_eq!(MyPluginEnum::Bar.into_glib(), 1); assert_eq!(unsafe { MyPluginEnum::from_glib(0) }, MyPluginEnum::Foo); assert_eq!(unsafe { MyPluginEnum::from_glib(1) }, MyPluginEnum::Bar); let t = MyPluginEnum::static_type(); assert!(t.is_a(glib::Type::ENUM)); assert_eq!(t.name(), "MyPluginEnum"); let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); let values = e.values(); assert_eq!(values.len(), 2); assert_eq!(values[0].name(), "Foo"); assert_eq!(values[0].nick(), "foo"); assert_eq!(values[1].name(), "Bar"); assert_eq!(values[1].nick(), "bar"); let v = e.value(0).expect("EnumClass::get_value(0) failed"); assert_eq!(v.name(), "Foo"); assert_eq!(v.nick(), "foo"); let v = e.value(1).expect("EnumClass::get_value(1) failed"); assert_eq!(v.name(), "Bar"); assert_eq!(v.nick(), "bar"); assert_eq!(e.value(2), None); // within enums registered as dynamic types, values are usables only if // at least one type class ref exists (see `glib::EnumClass`). assert_eq!( MyPluginEnum::Foo.to_value().get::(), Ok(MyPluginEnum::Foo) ); assert_eq!( MyPluginEnum::Bar.to_value().get::(), Ok(MyPluginEnum::Bar) ); assert_eq!(MyPluginEnumLazy::Foo.into_glib(), 0); assert_eq!(MyPluginEnumLazy::Bar.into_glib(), 1); assert_eq!( unsafe { MyPluginEnumLazy::from_glib(0) }, MyPluginEnumLazy::Foo ); assert_eq!( unsafe { MyPluginEnumLazy::from_glib(1) }, MyPluginEnumLazy::Bar ); let t = MyPluginEnumLazy::static_type(); assert!(t.is_a(glib::Type::ENUM)); assert_eq!(t.name(), "MyPluginEnumLazy"); let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); let values = e.values(); assert_eq!(values.len(), 2); assert_eq!(values[0].name(), "Foo"); assert_eq!(values[0].nick(), "foo"); assert_eq!(values[1].name(), "Bar"); assert_eq!(values[1].nick(), "bar"); let v = e.value(0).expect("EnumClass::get_value(0) failed"); assert_eq!(v.name(), "Foo"); assert_eq!(v.nick(), "foo"); let v = e.value(1).expect("EnumClass::get_value(1) failed"); assert_eq!(v.name(), "Bar"); assert_eq!(v.nick(), "bar"); assert_eq!(e.value(2), None); // within enums registered as dynamic types, values are usables only if // at least one type class ref exists (see `glib::EnumClass`). assert_eq!( MyPluginEnumLazy::Foo.to_value().get::(), Ok(MyPluginEnumLazy::Foo) ); assert_eq!( MyPluginEnumLazy::Bar.to_value().get::(), Ok(MyPluginEnumLazy::Bar) ); // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(plugin); } } glib-macros-0.20.4/tests/flags_dynamic.rs000064400000000000000000000610271046102023000164240ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use glib::{prelude::*, subclass::prelude::*}; mod module { use super::*; mod imp { use super::*; // impl for a type module (must extend `glib::TypeModule` and must implement `glib::TypePlugin`). #[derive(Default)] pub struct MyModule; #[glib::object_subclass] impl ObjectSubclass for MyModule { const NAME: &'static str = "MyModule"; type Type = super::MyModule; type ParentType = glib::TypeModule; type Interfaces = (glib::TypePlugin,); } impl ObjectImpl for MyModule {} impl TypePluginImpl for MyModule {} impl TypeModuleImpl for MyModule { fn load(&self) -> bool { // registers flags as dynamic types. let my_module = self.obj(); let type_module: &glib::TypeModule = my_module.upcast_ref(); super::MyModuleFlags::on_implementation_load(type_module) && super::MyModuleFlagsLazy::on_implementation_load(type_module) } fn unload(&self) { // marks the flags as unregistered. let my_module = self.obj(); let type_module: &glib::TypeModule = my_module.upcast_ref(); super::MyModuleFlagsLazy::on_implementation_unload(type_module); super::MyModuleFlags::on_implementation_unload(type_module); } } } // flags to register as a dynamic type. #[glib::flags(name = "MyModuleFlags")] #[flags_dynamic] enum MyModuleFlags { #[flags_value(name = "Flag A", nick = "nick-a")] A = 0b00000001, #[flags_value(name = "Flag B")] B = 0b00000010, #[flags_value(skip)] AB = Self::A.bits() | Self::B.bits(), C = 0b00000100, } // flags to lazy register as a dynamic type. #[glib::flags(name = "MyModuleFlagsLazy")] #[flags_dynamic(lazy_registration = true)] enum MyModuleFlagsLazy { #[flags_value(name = "Flag A", nick = "nick-a")] A = 0b00000001, #[flags_value(name = "Flag B")] B = 0b00000010, #[flags_value(skip)] AB = Self::A.bits() | Self::B.bits(), C = 0b00000100, } // a module (must extend `glib::TypeModule` and must implement `glib::TypePlugin`). glib::wrapper! { pub struct MyModule(ObjectSubclass) @extends glib::TypeModule, @implements glib::TypePlugin; } #[test] fn dynamic_flags() { // 1st: creates a single module to test with. let module = glib::Object::new::(); // 1st: uses it to test lifecycle of flags registered as dynamic types. dynamic_flags_lifecycle(&module); // 2nd: uses it to test behavior of flags registered as dynamic types. dynamic_flags_behavior(&module); } // tests lifecycle of flags registered as dynamic types within a module. fn dynamic_flags_lifecycle(module: &MyModule) { // checks types of flags to register as dynamic types are invalid (module is not loaded yet). assert!(!MyModuleFlags::static_type().is_valid()); assert!(!MyModuleFlagsLazy::static_type().is_valid()); // simulates the GLib type system to load/unload the module. TypeModuleExt::use_(module); TypeModuleExt::unuse(module); // checks types of flags registered as dynamic types are valid (module is unloaded). assert!(MyModuleFlags::static_type().is_valid()); // checks types of flags that are lazy registered as dynamic types are valid (module is unloaded). assert!(!MyModuleFlagsLazy::static_type().is_valid()); // simulates the GLib type system to load the module. TypeModuleExt::use_(module); // checks types of flags registered as dynamic types are valid (module is loaded). let flags_type = MyModuleFlags::static_type(); assert!(flags_type.is_valid()); let flags_lazy_type = MyModuleFlagsLazy::static_type(); assert!(flags_lazy_type.is_valid()); // checks plugin of flags registered as dynamic types is `MyModule`. assert_eq!( flags_type.plugin().as_ref(), Some(module.upcast_ref::()) ); assert_eq!( flags_lazy_type.plugin().as_ref(), Some(module.upcast_ref::()) ); // simulates the GLib type system to unload the module. TypeModuleExt::unuse(module); // checks types of flags registered as dynamic types are still valid (should have been marked as unloaded by the GLib type system but this cannot be checked). assert!(MyModuleFlags::static_type().is_valid()); assert!(MyModuleFlagsLazy::static_type().is_valid()); // simulates the GLib type system to reload the module. TypeModuleExt::use_(module); // checks types of flags registered as dynamic types are still valid (should have been marked as loaded by the GLib type system but this cannot be checked). assert!(MyModuleFlags::static_type().is_valid()); assert!(MyModuleFlagsLazy::static_type().is_valid()); // simulates the GLib type system to unload the module. TypeModuleExt::unuse(module); } // tests behavior of flags registered as dynamic types within a module. fn dynamic_flags_behavior(module: &MyModule) { use glib::prelude::*; use glib::translate::{FromGlib, IntoGlib}; // simulates the GLib type system to load the module. TypeModuleExt::use_(module); assert_eq!(MyModuleFlags::A.bits(), 1); assert_eq!(MyModuleFlags::B.bits(), 2); assert_eq!(MyModuleFlags::AB.bits(), 3); assert_eq!(MyModuleFlags::empty().into_glib(), 0); assert_eq!(MyModuleFlags::A.into_glib(), 1); assert_eq!(MyModuleFlags::B.into_glib(), 2); assert_eq!(MyModuleFlags::AB.into_glib(), 3); assert_eq!( unsafe { MyModuleFlags::from_glib(0) }, MyModuleFlags::empty() ); assert_eq!(unsafe { MyModuleFlags::from_glib(1) }, MyModuleFlags::A); assert_eq!(unsafe { MyModuleFlags::from_glib(2) }, MyModuleFlags::B); assert_eq!(unsafe { MyModuleFlags::from_glib(3) }, MyModuleFlags::AB); let t = MyModuleFlags::static_type(); assert!(t.is_a(glib::Type::FLAGS)); assert_eq!(t.name(), "MyModuleFlags"); let e = glib::FlagsClass::with_type(t).expect("FlagsClass::new failed"); let values = e.values(); assert_eq!(values.len(), 3); assert_eq!(values[0].name(), "Flag A"); assert_eq!(values[0].nick(), "nick-a"); assert_eq!(values[1].name(), "Flag B"); assert_eq!(values[1].nick(), "b"); assert_eq!(values[2].name(), "C"); assert_eq!(values[2].nick(), "c"); let v = e.value(1).expect("FlagsClass::get_value(1) failed"); assert_eq!(v.name(), "Flag A"); assert_eq!(v.nick(), "nick-a"); let v = e.value(2).expect("FlagsClass::get_value(2) failed"); assert_eq!(v.name(), "Flag B"); assert_eq!(v.nick(), "b"); let v = e.value(4).expect("FlagsClass::get_value(4) failed"); assert_eq!(v.name(), "C"); assert_eq!(v.nick(), "c"); // within flags registered as dynamic types, values are usables only if // at least one type class ref exists (see `glib::FlagsClass`). assert_eq!( MyModuleFlags::empty().to_value().get::(), Ok(MyModuleFlags::empty()) ); assert_eq!( MyModuleFlags::A.to_value().get::(), Ok(MyModuleFlags::A) ); assert_eq!( MyModuleFlags::B.to_value().get::(), Ok(MyModuleFlags::B) ); assert_eq!( MyModuleFlags::AB.to_value().get::(), Ok(MyModuleFlags::AB) ); assert!(e.value_by_name("Flag A").is_some()); assert!(e.value_by_name("Flag B").is_some()); assert!(e.value_by_name("AB").is_none()); assert!(e.value_by_name("C").is_some()); assert!(e.value_by_nick("nick-a").is_some()); assert!(e.value_by_nick("b").is_some()); assert!(e.value_by_nick("ab").is_none()); assert!(e.value_by_nick("c").is_some()); assert_eq!(MyModuleFlagsLazy::A.bits(), 1); assert_eq!(MyModuleFlagsLazy::B.bits(), 2); assert_eq!(MyModuleFlagsLazy::AB.bits(), 3); assert_eq!(MyModuleFlagsLazy::empty().into_glib(), 0); assert_eq!(MyModuleFlagsLazy::A.into_glib(), 1); assert_eq!(MyModuleFlagsLazy::B.into_glib(), 2); assert_eq!(MyModuleFlagsLazy::AB.into_glib(), 3); assert_eq!( unsafe { MyModuleFlagsLazy::from_glib(0) }, MyModuleFlagsLazy::empty() ); assert_eq!( unsafe { MyModuleFlagsLazy::from_glib(1) }, MyModuleFlagsLazy::A ); assert_eq!( unsafe { MyModuleFlagsLazy::from_glib(2) }, MyModuleFlagsLazy::B ); assert_eq!( unsafe { MyModuleFlagsLazy::from_glib(3) }, MyModuleFlagsLazy::AB ); let t = MyModuleFlagsLazy::static_type(); assert!(t.is_a(glib::Type::FLAGS)); assert_eq!(t.name(), "MyModuleFlagsLazy"); let e = glib::FlagsClass::with_type(t).expect("FlagsClass::new failed"); let values = e.values(); assert_eq!(values.len(), 3); assert_eq!(values[0].name(), "Flag A"); assert_eq!(values[0].nick(), "nick-a"); assert_eq!(values[1].name(), "Flag B"); assert_eq!(values[1].nick(), "b"); assert_eq!(values[2].name(), "C"); assert_eq!(values[2].nick(), "c"); let v = e.value(1).expect("FlagsClass::get_value(1) failed"); assert_eq!(v.name(), "Flag A"); assert_eq!(v.nick(), "nick-a"); let v = e.value(2).expect("FlagsClass::get_value(2) failed"); assert_eq!(v.name(), "Flag B"); assert_eq!(v.nick(), "b"); let v = e.value(4).expect("FlagsClass::get_value(4) failed"); assert_eq!(v.name(), "C"); assert_eq!(v.nick(), "c"); // within flags registered as dynamic types, values are usables only if // at least one type class ref exists (see `glib::FlagsClass`). assert_eq!( MyModuleFlagsLazy::empty() .to_value() .get::(), Ok(MyModuleFlagsLazy::empty()) ); assert_eq!( MyModuleFlagsLazy::A.to_value().get::(), Ok(MyModuleFlagsLazy::A) ); assert_eq!( MyModuleFlagsLazy::B.to_value().get::(), Ok(MyModuleFlagsLazy::B) ); assert_eq!( MyModuleFlagsLazy::AB.to_value().get::(), Ok(MyModuleFlagsLazy::AB) ); assert!(e.value_by_name("Flag A").is_some()); assert!(e.value_by_name("Flag B").is_some()); assert!(e.value_by_name("AB").is_none()); assert!(e.value_by_name("C").is_some()); assert!(e.value_by_nick("nick-a").is_some()); assert!(e.value_by_nick("b").is_some()); assert!(e.value_by_nick("ab").is_none()); assert!(e.value_by_nick("c").is_some()); // simulates the GLib type system to unload the module. TypeModuleExt::unuse(module); } } pub mod plugin { use super::*; pub mod imp { use glib::FlagsClass; use super::*; use std::cell::Cell; // impl for a type plugin (must implement `glib::TypePlugin`). #[derive(Default)] pub struct MyPlugin { my_flags_type_values: Cell>, my_flags_lazy_type_values: Cell>, } #[glib::object_subclass] impl ObjectSubclass for MyPlugin { const NAME: &'static str = "MyPlugin"; type Type = super::MyPlugin; type Interfaces = (glib::TypePlugin,); } impl ObjectImpl for MyPlugin {} impl TypePluginImpl for MyPlugin { fn use_plugin(&self) { // register flags as dynamic types. let my_plugin = self.obj(); super::MyPluginFlags::on_implementation_load(my_plugin.as_ref()); super::MyPluginFlagsLazy::on_implementation_load(my_plugin.as_ref()); } fn unuse_plugin(&self) { // marks flags as unregistered. let my_plugin = self.obj(); super::MyPluginFlagsLazy::on_implementation_unload(my_plugin.as_ref()); super::MyPluginFlags::on_implementation_unload(my_plugin.as_ref()); } fn complete_type_info( &self, type_: glib::Type, ) -> (glib::TypeInfo, glib::TypeValueTable) { let flags_type_values = match type_ { type_ if type_ == super::MyPluginFlags::static_type() => { self.my_flags_type_values.get() } type_ if type_ == super::MyPluginFlagsLazy::static_type() => { self.my_flags_lazy_type_values.get() } _ => panic!("unexpected type"), } .expect("flags type values"); let type_info = FlagsClass::complete_type_info(type_, flags_type_values) .expect("FlagsClass::type_info failed"); (type_info, glib::TypeValueTable::default()) } } impl TypePluginRegisterImpl for MyPlugin { fn register_dynamic_flags( &self, type_name: &str, const_static_values: &'static glib::enums::FlagsValues, ) -> glib::Type { let type_ = glib::Type::from_name(type_name).unwrap_or_else(|| { glib::Type::register_dynamic( glib::Type::FLAGS, type_name, self.obj().upcast_ref::(), glib::TypeFlags::NONE, ) }); if type_.is_valid() { match type_name { "MyPluginFlags" => self.my_flags_type_values.set(Some(const_static_values)), "MyPluginFlagsLazy" => self .my_flags_lazy_type_values .set(Some(const_static_values)), _ => panic!("unexpected"), }; } type_ } } } // flags to register as a dynamic type. #[glib::flags(name = "MyPluginFlags")] #[flags_dynamic(plugin_type = MyPlugin)] enum MyPluginFlags { #[flags_value(name = "Flag A", nick = "nick-a")] A = 0b00000001, #[flags_value(name = "Flag B")] B = 0b00000010, #[flags_value(skip)] AB = Self::A.bits() | Self::B.bits(), C = 0b00000100, } // flags to lazy register as a dynamic type. #[glib::flags(name = "MyPluginFlagsLazy")] #[flags_dynamic(plugin_type = MyPlugin, lazy_registration = true)] enum MyPluginFlagsLazy { #[flags_value(name = "Flag A", nick = "nick-a")] A = 0b00000001, #[flags_value(name = "Flag B")] B = 0b00000010, #[flags_value(skip)] AB = Self::A.bits() | Self::B.bits(), C = 0b00000100, } // a plugin (must implement `glib::TypePlugin`). glib::wrapper! { pub struct MyPlugin(ObjectSubclass) @implements glib::TypePlugin; } #[test] fn dynamic_flags() { // 1st: creates a single plugin to test with. let plugin = glib::Object::new::(); // 1st: uses it to test lifecycle of flags registered as dynamic types. dynamic_flags_lifecycle(&plugin); // 2nd: uses it to test behavior of flags registered as dynamic types. dynamic_flags_behavior(&plugin); } // tests lifecycle of flags registered as dynamic types within a plugin. fn dynamic_flags_lifecycle(plugin: &MyPlugin) { use glib::prelude::*; // checks types of flags to register as dynamic types are invalid (plugin is not used yet). assert!(!MyPluginFlags::static_type().is_valid()); assert!(!MyPluginFlagsLazy::static_type().is_valid()); // simulates the GLib type system to use/unuse the plugin. TypePluginExt::use_(plugin); TypePluginExt::unuse(plugin); // checks types of flags registered as dynamic types are valid (plugin is unused). assert!(MyPluginFlags::static_type().is_valid()); // checks types of flags that are lazy registered as dynamic types are still invalid (plugin is unused). assert!(!MyPluginFlagsLazy::static_type().is_valid()); // simulates the GLib type system to use the plugin. TypePluginExt::use_(plugin); // checks types of flags registered as dynamic types are valid (plugin is used). let flags_type = MyPluginFlags::static_type(); assert!(flags_type.is_valid()); let flags_lazy_type = MyPluginFlagsLazy::static_type(); assert!(flags_lazy_type.is_valid()); // checks plugin of flags registered as dynamic types is `MyPlugin`. assert_eq!( flags_type.plugin().as_ref(), Some(plugin.upcast_ref::()) ); assert_eq!( flags_lazy_type.plugin().as_ref(), Some(plugin.upcast_ref::()) ); // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(plugin); // checks types of flags registered as dynamic types are still valid. assert!(MyPluginFlags::static_type().is_valid()); assert!(MyPluginFlagsLazy::static_type().is_valid()); // simulates the GLib type system to reuse the plugin. TypePluginExt::use_(plugin); // checks types of flags registered as dynamic types are still valid. assert!(MyPluginFlags::static_type().is_valid()); assert!(MyPluginFlagsLazy::static_type().is_valid()); // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(plugin); } // tests behavior of flags registered as dynamic types within a plugin. fn dynamic_flags_behavior(plugin: &MyPlugin) { use glib::prelude::*; use glib::translate::{FromGlib, IntoGlib}; // simulates the GLib type system to use the plugin. TypePluginExt::use_(plugin); assert_eq!(MyPluginFlags::A.bits(), 1); assert_eq!(MyPluginFlags::B.bits(), 2); assert_eq!(MyPluginFlags::AB.bits(), 3); assert_eq!(MyPluginFlags::empty().into_glib(), 0); assert_eq!(MyPluginFlags::A.into_glib(), 1); assert_eq!(MyPluginFlags::B.into_glib(), 2); assert_eq!(MyPluginFlags::AB.into_glib(), 3); assert_eq!( unsafe { MyPluginFlags::from_glib(0) }, MyPluginFlags::empty() ); assert_eq!(unsafe { MyPluginFlags::from_glib(1) }, MyPluginFlags::A); assert_eq!(unsafe { MyPluginFlags::from_glib(2) }, MyPluginFlags::B); assert_eq!(unsafe { MyPluginFlags::from_glib(3) }, MyPluginFlags::AB); let t = MyPluginFlags::static_type(); assert!(t.is_a(glib::Type::FLAGS)); assert_eq!(t.name(), "MyPluginFlags"); let e = glib::FlagsClass::with_type(t).expect("FlagsClass::new failed"); let values = e.values(); assert_eq!(values.len(), 3); assert_eq!(values[0].name(), "Flag A"); assert_eq!(values[0].nick(), "nick-a"); assert_eq!(values[1].name(), "Flag B"); assert_eq!(values[1].nick(), "b"); assert_eq!(values[2].name(), "C"); assert_eq!(values[2].nick(), "c"); let v = e.value(1).expect("FlagsClass::get_value(1) failed"); assert_eq!(v.name(), "Flag A"); assert_eq!(v.nick(), "nick-a"); let v = e.value(2).expect("FlagsClass::get_value(2) failed"); assert_eq!(v.name(), "Flag B"); assert_eq!(v.nick(), "b"); let v = e.value(4).expect("FlagsClass::get_value(4) failed"); assert_eq!(v.name(), "C"); assert_eq!(v.nick(), "c"); // within flags registered as dynamic types, values are usables only if // at least one type class ref exists (see `glib::FlagsClass`). assert_eq!( MyPluginFlags::empty().to_value().get::(), Ok(MyPluginFlags::empty()) ); assert_eq!( MyPluginFlags::A.to_value().get::(), Ok(MyPluginFlags::A) ); assert_eq!( MyPluginFlags::B.to_value().get::(), Ok(MyPluginFlags::B) ); assert_eq!( MyPluginFlags::AB.to_value().get::(), Ok(MyPluginFlags::AB) ); assert!(e.value_by_name("Flag A").is_some()); assert!(e.value_by_name("Flag B").is_some()); assert!(e.value_by_name("AB").is_none()); assert!(e.value_by_name("C").is_some()); assert!(e.value_by_nick("nick-a").is_some()); assert!(e.value_by_nick("b").is_some()); assert!(e.value_by_nick("ab").is_none()); assert!(e.value_by_nick("c").is_some()); assert_eq!(MyPluginFlagsLazy::A.bits(), 1); assert_eq!(MyPluginFlagsLazy::B.bits(), 2); assert_eq!(MyPluginFlagsLazy::AB.bits(), 3); assert_eq!(MyPluginFlagsLazy::empty().into_glib(), 0); assert_eq!(MyPluginFlagsLazy::A.into_glib(), 1); assert_eq!(MyPluginFlagsLazy::B.into_glib(), 2); assert_eq!(MyPluginFlagsLazy::AB.into_glib(), 3); assert_eq!( unsafe { MyPluginFlagsLazy::from_glib(0) }, MyPluginFlagsLazy::empty() ); assert_eq!( unsafe { MyPluginFlagsLazy::from_glib(1) }, MyPluginFlagsLazy::A ); assert_eq!( unsafe { MyPluginFlagsLazy::from_glib(2) }, MyPluginFlagsLazy::B ); assert_eq!( unsafe { MyPluginFlagsLazy::from_glib(3) }, MyPluginFlagsLazy::AB ); let t = MyPluginFlagsLazy::static_type(); assert!(t.is_a(glib::Type::FLAGS)); assert_eq!(t.name(), "MyPluginFlagsLazy"); let e = glib::FlagsClass::with_type(t).expect("FlagsClass::new failed"); let values = e.values(); assert_eq!(values.len(), 3); assert_eq!(values[0].name(), "Flag A"); assert_eq!(values[0].nick(), "nick-a"); assert_eq!(values[1].name(), "Flag B"); assert_eq!(values[1].nick(), "b"); assert_eq!(values[2].name(), "C"); assert_eq!(values[2].nick(), "c"); let v = e.value(1).expect("FlagsClass::get_value(1) failed"); assert_eq!(v.name(), "Flag A"); assert_eq!(v.nick(), "nick-a"); let v = e.value(2).expect("FlagsClass::get_value(2) failed"); assert_eq!(v.name(), "Flag B"); assert_eq!(v.nick(), "b"); let v = e.value(4).expect("FlagsClass::get_value(4) failed"); assert_eq!(v.name(), "C"); assert_eq!(v.nick(), "c"); // within flags registered as dynamic types, values are usables only if // at least one type class ref exists (see `glib::FlagsClass`). assert_eq!( MyPluginFlagsLazy::empty() .to_value() .get::(), Ok(MyPluginFlagsLazy::empty()) ); assert_eq!( MyPluginFlagsLazy::A.to_value().get::(), Ok(MyPluginFlagsLazy::A) ); assert_eq!( MyPluginFlagsLazy::B.to_value().get::(), Ok(MyPluginFlagsLazy::B) ); assert_eq!( MyPluginFlagsLazy::AB.to_value().get::(), Ok(MyPluginFlagsLazy::AB) ); assert!(e.value_by_name("Flag A").is_some()); assert!(e.value_by_name("Flag B").is_some()); assert!(e.value_by_name("AB").is_none()); assert!(e.value_by_name("C").is_some()); assert!(e.value_by_nick("nick-a").is_some()); assert!(e.value_by_nick("b").is_some()); assert!(e.value_by_nick("ab").is_none()); assert!(e.value_by_nick("c").is_some()); // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(plugin); } } glib-macros-0.20.4/tests/object_subclass_dynamic.rs000064400000000000000000000643351046102023000205020ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use glib::{prelude::*, subclass::prelude::*}; mod static_ { use super::*; pub mod imp { use super::*; // impl for an object interface to register as a static type. #[derive(Clone, Copy)] #[repr(C)] pub struct MyStaticInterfaceClass { parent: glib::gobject_ffi::GTypeInterface, } unsafe impl InterfaceStruct for MyStaticInterfaceClass { type Type = MyStaticInterface; } pub enum MyStaticInterface {} #[glib::object_interface] impl ObjectInterface for MyStaticInterface { const NAME: &'static str = "MyStaticInterface"; type Interface = MyStaticInterfaceClass; } pub trait MyStaticInterfaceImpl: ObjectImpl + ObjectSubclass {} // impl for an object subclass to register as a static type and that implements `MyStaticInterface`. #[derive(Default)] pub struct MyStaticType; #[glib::object_subclass] impl ObjectSubclass for MyStaticType { const NAME: &'static str = "MyStaticType"; type Type = super::MyStaticType; type Interfaces = (super::MyStaticInterface,); } impl ObjectImpl for MyStaticType {} impl MyStaticInterfaceImpl for MyStaticType {} pub trait MyStaticTypeImpl: ObjectImpl + ObjectSubclass {} } // an object interface to register as a static type. glib::wrapper! { pub struct MyStaticInterface(ObjectInterface); } unsafe impl IsImplementable for MyStaticInterface {} // an object subclass to register as a static type and that implements `MyStaticInterface`. glib::wrapper! { pub struct MyStaticType(ObjectSubclass) @implements MyStaticInterface; } unsafe impl IsSubclassable for MyStaticType {} } use static_::{ imp::{MyStaticInterfaceImpl, MyStaticTypeImpl}, *, }; mod module { use super::*; mod imp { use super::*; // impl for a object interface to register as a dynamic type and that extends `MyStaticInterface`. #[derive(Clone, Copy)] #[repr(C)] pub struct MyModuleInterfaceClass { parent: glib::gobject_ffi::GTypeInterface, } unsafe impl InterfaceStruct for MyModuleInterfaceClass { type Type = MyModuleInterface; } pub enum MyModuleInterface {} #[glib::object_interface] #[object_interface_dynamic] impl ObjectInterface for MyModuleInterface { const NAME: &'static str = "MyModuleInterface"; type Prerequisites = (MyStaticInterface,); type Interface = MyModuleInterfaceClass; } pub trait MyModuleInterfaceImpl: ObjectImpl + ObjectSubclass {} // impl for an object subclass to register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyModuleInterface`. #[derive(Default)] pub struct MyModuleType; #[glib::object_subclass] #[object_subclass_dynamic] impl ObjectSubclass for MyModuleType { const NAME: &'static str = "MyModuleType"; type Type = super::MyModuleType; type ParentType = MyStaticType; type Interfaces = (MyStaticInterface, super::MyModuleInterface); } impl ObjectImpl for MyModuleType {} impl MyStaticTypeImpl for MyModuleType {} impl MyStaticInterfaceImpl for MyModuleType {} impl MyModuleInterfaceImpl for MyModuleType {} // impl for an object interface to lazy register as a dynamic type and that extends `MyStaticInterface`. #[derive(Clone, Copy)] #[repr(C)] pub struct MyModuleInterfaceLazyClass { parent: glib::gobject_ffi::GTypeInterface, } unsafe impl InterfaceStruct for MyModuleInterfaceLazyClass { type Type = MyModuleInterfaceLazy; } pub enum MyModuleInterfaceLazy {} #[glib::object_interface] #[object_interface_dynamic(lazy_registration = true)] impl ObjectInterface for MyModuleInterfaceLazy { const NAME: &'static str = "MyModuleInterfaceLazy"; type Prerequisites = (MyStaticInterface,); type Interface = MyModuleInterfaceLazyClass; } pub trait MyModuleInterfaceLazyImpl: ObjectImpl + ObjectSubclass {} // impl for an object subclass to lazy register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyModuleInterfaceLazy`. #[derive(Default)] pub struct MyModuleTypeLazy; #[glib::object_subclass] #[object_subclass_dynamic(lazy_registration = true)] impl ObjectSubclass for MyModuleTypeLazy { const NAME: &'static str = "MyModuleTypeLazy"; type Type = super::MyModuleTypeLazy; type ParentType = MyStaticType; type Interfaces = (MyStaticInterface, super::MyModuleInterfaceLazy); } impl ObjectImpl for MyModuleTypeLazy {} impl MyStaticTypeImpl for MyModuleTypeLazy {} impl MyStaticInterfaceImpl for MyModuleTypeLazy {} impl MyModuleInterfaceLazyImpl for MyModuleTypeLazy {} // impl for a type module (must extend `glib::TypeModule` and must implement `glib::TypePlugin`). #[derive(Default)] pub struct MyModule; #[glib::object_subclass] impl ObjectSubclass for MyModule { const NAME: &'static str = "MyModule"; type Type = super::MyModule; type ParentType = glib::TypeModule; type Interfaces = (glib::TypePlugin,); } impl ObjectImpl for MyModule {} impl TypePluginImpl for MyModule {} impl TypeModuleImpl for MyModule { fn load(&self) -> bool { // registers object subclasses and interfaces as dynamic types. let my_module = self.obj(); let type_module: &glib::TypeModule = my_module.upcast_ref(); MyModuleInterface::on_implementation_load(type_module) && MyModuleType::on_implementation_load(type_module) && MyModuleInterfaceLazy::on_implementation_load(type_module) && MyModuleTypeLazy::on_implementation_load(type_module) } fn unload(&self) { // marks object subclasses and interfaces as unregistered. let my_module = self.obj(); let type_module: &glib::TypeModule = my_module.upcast_ref(); MyModuleTypeLazy::on_implementation_unload(type_module); MyModuleInterfaceLazy::on_implementation_unload(type_module); MyModuleType::on_implementation_unload(type_module); MyModuleInterface::on_implementation_unload(type_module); } } } // an object interface to register as a dynamic type and that extends `MyStaticInterface`. glib::wrapper! { pub struct MyModuleInterface(ObjectInterface) @requires MyStaticInterface; } unsafe impl IsImplementable for MyModuleInterface {} // an object subclass to register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyModuleInterface`. glib::wrapper! { pub struct MyModuleType(ObjectSubclass) @extends MyStaticType, @implements MyStaticInterface, MyModuleInterface; } // an object interface to lazy register as a dynamic type and that extends `MyStaticInterface`. glib::wrapper! { pub struct MyModuleInterfaceLazy(ObjectInterface) @requires MyStaticInterface; } unsafe impl IsImplementable for MyModuleInterfaceLazy {} // an object subclass to lazy register as a dynamic type and that extends `MyStaticType` that implements `MyStaticInterface` and `MyModuleInterfaceLazy`. glib::wrapper! { pub struct MyModuleTypeLazy(ObjectSubclass) @extends MyStaticType, @implements MyStaticInterface, MyModuleInterfaceLazy; } // a module (must extend `glib::TypeModule` and must implement `glib::TypePlugin`). glib::wrapper! { pub struct MyModule(ObjectSubclass) @extends glib::TypeModule, @implements glib::TypePlugin; } #[test] fn dynamic_object_subclasses() { use glib::prelude::TypeModuleExt; // checks types of object subclasses and of object interfaces to register as dynamic types are invalid (module is not loaded yet). assert!(!imp::MyModuleInterface::type_().is_valid()); assert!(!imp::MyModuleType::type_().is_valid()); assert!(!imp::MyModuleInterfaceLazy::type_().is_valid()); assert!(!imp::MyModuleTypeLazy::type_().is_valid()); // simulates the GLib type system to load/unload the module. let module = glib::Object::new::(); TypeModuleExt::use_(&module); TypeModuleExt::unuse(&module); // checks types of object subclasses and of object interfaces registered as dynamic types are valid (module is unloaded). assert!(imp::MyModuleInterface::type_().is_valid()); assert!(imp::MyModuleType::type_().is_valid()); // checks types of object subclasses and of object interfaces that are lazy registered as dynamic types are still invalid (module is unloaded). assert!(!imp::MyModuleInterfaceLazy::type_().is_valid()); assert!(!imp::MyModuleTypeLazy::type_().is_valid()); // simulates the GLib type system to load the module. TypeModuleExt::use_(&module); // checks types of object subclasses and of object interfaces registered as dynamic types are valid (module is loaded). let iface_type = imp::MyModuleInterface::type_(); assert!(iface_type.is_valid()); let obj_type = imp::MyModuleType::type_(); assert!(obj_type.is_valid()); let iface_lazy_type = imp::MyModuleInterfaceLazy::type_(); assert!(iface_lazy_type.is_valid()); let obj_lazy_type = imp::MyModuleTypeLazy::type_(); assert!(obj_lazy_type.is_valid()); // checks plugin of object subclasses and of object interfaces is `MyModule`. assert_eq!( iface_type.plugin().as_ref(), Some(module.upcast_ref::()) ); assert_eq!( obj_type.plugin().as_ref(), Some(module.upcast_ref::()) ); assert_eq!( iface_lazy_type.plugin().as_ref(), Some(module.upcast_ref::()) ); assert_eq!( obj_lazy_type.plugin().as_ref(), Some(module.upcast_ref::()) ); // simulates the GLib type system to unload the module. TypeModuleExt::unuse(&module); // checks types of object subclasses and of object interfaces registered as dynamic types are still valid (should have been marked as unloaded by the GLib type system but this cannot be checked). assert!(imp::MyModuleInterface::type_().is_valid()); assert!(imp::MyModuleType::type_().is_valid()); assert!(imp::MyModuleInterfaceLazy::type_().is_valid()); assert!(imp::MyModuleTypeLazy::type_().is_valid()); // simulates the GLib type system to reload the module. TypeModuleExt::use_(&module); // checks types of object subclasses and of object interfaces registered as dynamic types are still valid (should have been marked as unloaded by the GLib type system but this cannot be checked). assert!(imp::MyModuleInterface::type_().is_valid()); assert!(imp::MyModuleType::type_().is_valid()); assert!(imp::MyModuleInterfaceLazy::type_().is_valid()); assert!(imp::MyModuleTypeLazy::type_().is_valid()); // simulates the GLib type system to unload the module. TypeModuleExt::unuse(&module); } } pub mod plugin { use super::*; pub mod imp { use super::*; use std::cell::Cell; // impl for a object interface to register as a dynamic type and that extends `MyStaticInterface`. #[derive(Clone, Copy)] #[repr(C)] pub struct MyPluginInterfaceClass { parent: glib::gobject_ffi::GTypeInterface, } unsafe impl InterfaceStruct for MyPluginInterfaceClass { type Type = MyPluginInterface; } pub enum MyPluginInterface {} #[glib::object_interface] #[object_interface_dynamic(plugin_type = super::MyPlugin)] impl ObjectInterface for MyPluginInterface { const NAME: &'static str = "MyPluginInterface"; type Prerequisites = (MyStaticInterface,); type Interface = MyPluginInterfaceClass; } pub trait MyPluginInterfaceImpl: ObjectImpl + ObjectSubclass {} // impl for an object subclass to register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyPluginInterface`. #[derive(Default)] pub struct MyPluginType; #[glib::object_subclass] #[object_subclass_dynamic(plugin_type = super::MyPlugin)] impl ObjectSubclass for MyPluginType { const NAME: &'static str = "MyPluginType"; type Type = super::MyPluginType; type ParentType = MyStaticType; type Interfaces = (MyStaticInterface, super::MyPluginInterface); } impl ObjectImpl for MyPluginType {} impl MyStaticTypeImpl for MyPluginType {} impl MyStaticInterfaceImpl for MyPluginType {} impl MyPluginInterfaceImpl for MyPluginType {} // impl for an object interface to lazy register as a dynamic type and that extends `MyStaticInterface`. #[derive(Clone, Copy)] #[repr(C)] pub struct MyPluginInterfaceLazyClass { parent: glib::gobject_ffi::GTypeInterface, } unsafe impl InterfaceStruct for MyPluginInterfaceLazyClass { type Type = MyPluginInterfaceLazy; } pub enum MyPluginInterfaceLazy {} #[glib::object_interface] #[object_interface_dynamic(plugin_type = super::MyPlugin, lazy_registration = true)] impl ObjectInterface for MyPluginInterfaceLazy { const NAME: &'static str = "MyPluginInterfaceLazy"; type Prerequisites = (MyStaticInterface,); type Interface = MyPluginInterfaceLazyClass; } pub trait MyPluginInterfaceLazyImpl: ObjectImpl + ObjectSubclass {} // impl for an object subclass to lazy register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyPluginInterfaceLazy`. #[derive(Default)] pub struct MyPluginTypeLazy; #[glib::object_subclass] #[object_subclass_dynamic(plugin_type = super::MyPlugin, lazy_registration = true)] impl ObjectSubclass for MyPluginTypeLazy { const NAME: &'static str = "MyPluginTypeLazy"; type Type = super::MyPluginTypeLazy; type ParentType = MyStaticType; type Interfaces = (MyStaticInterface, super::MyPluginInterfaceLazy); } impl ObjectImpl for MyPluginTypeLazy {} impl MyStaticTypeImpl for MyPluginTypeLazy {} impl MyStaticInterfaceImpl for MyPluginTypeLazy {} impl MyPluginInterfaceLazyImpl for MyPluginTypeLazy {} // impl for a type plugin (must implement `glib::TypePlugin`). #[derive(Default)] pub struct MyPlugin { my_interface_type_info: Cell>, my_interface_iface_info: Cell>, my_type_type_info: Cell>, my_interface_lazy_type_info: Cell>, my_interface_lazy_iface_info: Cell>, my_type_lazy_type_info: Cell>, } #[glib::object_subclass] impl ObjectSubclass for MyPlugin { const NAME: &'static str = "MyPlugin"; type Type = super::MyPlugin; type Interfaces = (glib::TypePlugin,); } impl ObjectImpl for MyPlugin {} impl TypePluginImpl for MyPlugin { fn use_plugin(&self) { // registers object subclasses and interfaces as dynamic types. let my_plugin = self.obj(); MyPluginInterface::on_implementation_load(my_plugin.as_ref()); MyPluginType::on_implementation_load(my_plugin.as_ref()); MyPluginInterfaceLazy::on_implementation_load(my_plugin.as_ref()); MyPluginTypeLazy::on_implementation_load(my_plugin.as_ref()); } fn unuse_plugin(&self) { // marks object subclasses and interfaces as unregistered. let my_plugin = self.obj(); MyPluginTypeLazy::on_implementation_unload(my_plugin.as_ref()); MyPluginInterfaceLazy::on_implementation_unload(my_plugin.as_ref()); MyPluginType::on_implementation_unload(my_plugin.as_ref()); MyPluginInterface::on_implementation_unload(my_plugin.as_ref()); } fn complete_type_info( &self, type_: glib::Type, ) -> (glib::TypeInfo, glib::TypeValueTable) { let info = match type_ { type_ if type_ == MyPluginType::type_() => self.my_type_type_info.get(), type_ if type_ == MyPluginInterface::type_() => { self.my_interface_type_info.get() } type_ if type_ == MyPluginTypeLazy::type_() => { self.my_type_lazy_type_info.get() } type_ if type_ == MyPluginInterfaceLazy::type_() => { self.my_interface_lazy_type_info.get() } _ => panic!("unexpected"), }; match info { Some(info) => (info, glib::TypeValueTable::default()), _ => panic!("unexpected"), } } fn complete_interface_info( &self, _instance_type: glib::Type, interface_type: glib::Type, ) -> glib::InterfaceInfo { let info = match interface_type { type_ if type_ == MyPluginInterface::type_() => { self.my_interface_iface_info.get() } type_ if type_ == MyPluginInterfaceLazy::type_() => { self.my_interface_lazy_iface_info.get() } _ => panic!("unexpected"), }; match info { Some(info) => info, _ => panic!("unexpected"), } } } impl TypePluginRegisterImpl for MyPlugin { fn add_dynamic_interface( &self, instance_type: glib::Type, interface_type: glib::Type, interface_info: &glib::InterfaceInfo, ) { if !instance_type.is_a(interface_type) { instance_type.add_interface_dynamic( interface_type, self.obj().upcast_ref::(), ); } match interface_type { type_ if type_ == imp::MyPluginInterface::type_() => { self.my_interface_iface_info.set(Some(*interface_info)) } type_ if type_ == imp::MyPluginInterfaceLazy::type_() => { self.my_interface_lazy_iface_info.set(Some(*interface_info)) } _ => panic!("unexpected"), }; } fn register_dynamic_type( &self, parent_type: glib::Type, type_name: &str, type_info: &glib::TypeInfo, flags: glib::TypeFlags, ) -> glib::Type { let type_ = glib::Type::from_name(type_name).unwrap_or_else(|| { glib::Type::register_dynamic( parent_type, type_name, self.obj().upcast_ref::(), flags, ) }); if type_.is_valid() { match type_name { imp::MyPluginType::NAME => self.my_type_type_info.set(Some(*type_info)), imp::MyPluginInterface::NAME => { self.my_interface_type_info.set(Some(*type_info)) } imp::MyPluginTypeLazy::NAME => { self.my_type_lazy_type_info.set(Some(*type_info)) } imp::MyPluginInterfaceLazy::NAME => { self.my_interface_lazy_type_info.set(Some(*type_info)) } _ => panic!("unexpected"), }; } type_ } } } // an object interface to register as a dynamic type and that extends `MyStaticInterface`. glib::wrapper! { pub struct MyPluginInterface(ObjectInterface) @requires MyStaticInterface; } unsafe impl IsImplementable for MyPluginInterface {} // an object subclass to register as a dynamic type and that extends `MyStaticType` and that implements `MyStaticInterface` and `MyPluginInterface`. glib::wrapper! { pub struct MyPluginType(ObjectSubclass) @implements MyPluginInterface; } // an object interface to lazy register as a dynamic type and that extends `MyStaticInterface`. glib::wrapper! { pub struct MyPluginInterfaceLazy(ObjectInterface) @requires MyStaticInterface; } unsafe impl IsImplementable for MyPluginInterfaceLazy {} // an object subclass to lazy register as a dynamic type and that extends `MyStaticType` that implements `MyStaticInterface` and `MyPluginInterfaceLazy`. glib::wrapper! { pub struct MyPluginTypeLazy(ObjectSubclass) @implements MyPluginInterfaceLazy; } // a plugin (must implement `glib::TypePlugin`). glib::wrapper! { pub struct MyPlugin(ObjectSubclass) @implements glib::TypePlugin; } #[test] fn dynamic_object_subclasses() { use glib::prelude::TypePluginExt; // checks types of object subclasses and of object interfaces to register as dynamic types are invalid (plugin is not used yet). assert!(!imp::MyPluginInterface::type_().is_valid()); assert!(!imp::MyPluginType::type_().is_valid()); assert!(!imp::MyPluginInterfaceLazy::type_().is_valid()); assert!(!imp::MyPluginTypeLazy::type_().is_valid()); // simulates the GLib type system to use/unuse the plugin. let plugin = glib::Object::new::(); TypePluginExt::use_(&plugin); TypePluginExt::unuse(&plugin); // checks types of object subclasses and of object interfaces registered as dynamic types are valid (plugin is unused). assert!(imp::MyPluginInterface::type_().is_valid()); assert!(imp::MyPluginType::type_().is_valid()); // check types of object subclasses and of object interfaces that are lazy registered as dynamic types are still invalid (plugin is unused) assert!(!imp::MyPluginInterfaceLazy::type_().is_valid()); assert!(!imp::MyPluginTypeLazy::type_().is_valid()); // simulates the GLib type system to use the plugin. TypePluginExt::use_(&plugin); // checks types of object subclasses and of object interfaces registered as dynamic types are valid (plugin is used). let iface_type = imp::MyPluginInterface::type_(); assert!(iface_type.is_valid()); let obj_type = imp::MyPluginType::type_(); assert!(obj_type.is_valid()); let iface_lazy_type = imp::MyPluginInterfaceLazy::type_(); assert!(iface_lazy_type.is_valid()); let obj_lazy_type = imp::MyPluginTypeLazy::type_(); assert!(obj_lazy_type.is_valid()); // checks plugin of object subclasses and of object interfaces is `MyPlugin`. assert_eq!( iface_type.plugin().as_ref(), Some(plugin.upcast_ref::()) ); assert_eq!( obj_type.plugin().as_ref(), Some(plugin.upcast_ref::()) ); assert_eq!( iface_lazy_type.plugin().as_ref(), Some(plugin.upcast_ref::()) ); assert_eq!( obj_lazy_type.plugin().as_ref(), Some(plugin.upcast_ref::()) ); // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(&plugin); // checks types of object subclasses and of object interfaces registered as dynamic types are still valid. assert!(imp::MyPluginInterface::type_().is_valid()); assert!(imp::MyPluginType::type_().is_valid()); assert!(imp::MyPluginInterfaceLazy::type_().is_valid()); assert!(imp::MyPluginTypeLazy::type_().is_valid()); // simulates the GLib type system to reuse the plugin. TypePluginExt::use_(&plugin); // checks types of object subclasses and of object interfaces registered as dynamic types are still valid. assert!(imp::MyPluginInterface::type_().is_valid()); assert!(imp::MyPluginType::type_().is_valid()); assert!(imp::MyPluginInterfaceLazy::type_().is_valid()); assert!(imp::MyPluginTypeLazy::type_().is_valid()); // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(&plugin); } } glib-macros-0.20.4/tests/properties.rs000064400000000000000000000426501046102023000160210ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use glib::prelude::*; use glib::ParamFlags; #[cfg(test)] mod base { use glib::prelude::*; use glib::subclass::prelude::*; use glib_macros::Properties; use std::marker::PhantomData; pub mod imp { use super::*; #[derive(Properties, Default)] #[properties(wrapper_type = super::Base, ext_trait)] pub struct Base { #[property(get = Self::not_overridden)] overridden: PhantomData, #[property(get = Self::not_overridden)] not_overridden: PhantomData, } #[glib::derived_properties] impl ObjectImpl for Base {} #[glib::object_subclass] impl ObjectSubclass for Base { const NAME: &'static str = "MyBase"; type Type = super::Base; } impl Base { fn not_overridden(&self) -> u32 { 42 } } } glib::wrapper! { pub struct Base(ObjectSubclass); } unsafe impl IsSubclassable for Base {} } #[cfg(test)] mod foo { use glib::prelude::*; use glib::subclass::prelude::*; use glib_macros::{Properties, ValueDelegate}; use std::cell::Cell; use std::cell::RefCell; use std::marker::PhantomData; use std::sync::Mutex; use super::base::Base; #[derive(ValueDelegate, Default, Debug, PartialEq)] pub struct MyPropertyValue(pub i32); #[derive(Clone, Default, Debug, PartialEq, Eq, glib::Boxed)] #[boxed_type(name = "SimpleBoxedString")] pub struct SimpleBoxedString(pub String); #[derive(Copy, Default, Clone, Debug, PartialEq, Eq, glib::Enum)] #[enum_type(name = "SimpleEnum")] pub enum SimpleEnum { #[default] One, } #[derive(Default, Clone)] struct Author { name: String, nick: String, } pub mod imp { use std::{cell::OnceCell, rc::Rc}; use super::*; #[derive(Properties, Default)] #[properties(wrapper_type = super::Foo)] pub struct Foo { #[property(get, set)] bar: Mutex, #[property(get, set)] double: RefCell, #[property(get, set)] string_vec: RefCell>, #[property(get, set, builder(glib::VariantTy::DOUBLE))] variant: RefCell>, #[property(get, set, builder(&>::static_variant_type()))] variant2: RefCell>, #[property(get = |_| 42.0, set)] infer_inline_type: RefCell, // The following property doesn't store any data. The value of the property is calculated // when the value is accessed. #[property(get = Self::hello_world)] _buzz: PhantomData, #[property(get, set)] my_property_value: RefCell, #[property(get, set = Self::set_fizz, name = "fizz", nick = "fizz-nick", blurb = "short description stored in the GLib type system" )] fizz: RefCell, #[property(name = "author-name", get, set, type = String, member = name)] #[property(name = "author-nick", get, set, type = String, member = nick)] author: RefCell, #[property( type = String, get = |t: &Self| t.author.borrow().name.to_owned(), set = Self::set_author_name)] fake_field: PhantomData, #[property(get)] read_only_text: String, #[property(get, set, explicit_notify, lax_validation)] custom_flags: RefCell, #[property(get, set, default = "hello")] with_default: RefCell, #[property(get, set, builder())] simple_builder: RefCell, #[property(get, set, builder().minimum(0).maximum(5))] numeric_builder: RefCell, #[property(get, set, minimum = 0, maximum = 5)] builder_fields_without_builder: RefCell, #[property(get, set, builder('c'))] builder_with_required_param: RefCell, #[property(get, set)] boxed: RefCell, #[property(get, set, builder(SimpleEnum::One))] fenum: RefCell, #[property(get, set, nullable)] object: RefCell>, #[property(get, set, nullable)] optional: RefCell>, #[property(get, set)] smart_pointer: Rc>, #[property(get, set)] once_cell: OnceCell, #[property(get, set)] cell: Cell, #[property(get = Self::overridden, override_class = Base)] overridden: PhantomData, #[property(get, set)] weak_ref_prop: glib::WeakRef, #[property(get, set)] send_weak_ref_prop: glib::SendWeakRef, #[property(get, default_value = 0, construct_only)] construct_only_cell: OnceCell, #[property(get, set = Self::set_construct_only_custom, construct_only)] construct_only_custom_setter: OnceCell>, } #[glib::object_subclass] impl ObjectSubclass for Foo { const NAME: &'static str = "MyFoo"; type Type = super::Foo; type ParentType = Base; } #[glib::derived_properties] impl ObjectImpl for Foo {} impl Foo { fn set_author_name(&self, value: String) { self.author.borrow_mut().name = value; } fn hello_world(&self) -> String { String::from("Hello world!") } fn set_fizz(&self, value: String) { *self.fizz.borrow_mut() = format!("custom set: {}", value); } fn overridden(&self) -> u32 { 43 } fn set_construct_only_custom(&self, value: Option) { self.construct_only_custom_setter .set(value.map(|v| format!("custom set: {}", v))) .expect("Setter to be only called once"); } } /// Checks that `Properties` does not pollute the scope with /// trait imports, as it did in older versions. #[test] fn no_import_leaks() { // `vec.get` can match these methods (in order of precedence): // (1) ` as PropertyGet>::get` // (2) `<[String]>::get` through deref of `Vec` // Had the macro introduced `PropertyGet` into the scope, it would // resolve to (1), which we do not want. let vec: Vec = vec![String::new(); 2]; assert_eq!(vec.get(1), Some(&String::new())); } } glib::wrapper! { pub struct Foo(ObjectSubclass) @extends Base; } } #[test] fn props() { let myfoo: foo::Foo = glib::object::Object::new(); // Read values let bar: String = myfoo.property("bar"); assert_eq!(bar, "".to_string()); let string_vec: Vec = myfoo.property("string-vec"); assert!(string_vec.is_empty()); let my_property_value: foo::MyPropertyValue = myfoo.property("my-property-value"); assert_eq!(my_property_value, foo::MyPropertyValue(0)); let var: Option = myfoo.property("variant"); assert!(var.is_none()); // Set values myfoo.set_property("bar", "epic".to_value()); let bar: String = myfoo.property("bar"); assert_eq!(bar, "epic".to_string()); myfoo.set_property("string-vec", ["epic", "more epic"].to_value()); let string_vec: Vec = myfoo.property("string-vec"); assert_eq!( string_vec, vec!["epic".to_string(), "more epic".to_string()] ); let myv = Some(2.0f64.to_variant()); myfoo.set_property("variant", &myv); let var: Option = myfoo.property("variant"); assert_eq!(var, myv); // Custom getter let buzz: String = myfoo.property("buzz"); assert_eq!(buzz, "Hello world!".to_string()); // Custom setter myfoo.set_property("fizz", "test"); let fizz: String = myfoo.property("fizz"); assert_eq!(fizz, "custom set: test".to_string()); // Multiple props on the same field myfoo.set_property("author-name", "freddy".to_value()); let author_name: String = myfoo.property("author-name"); assert_eq!(author_name, "freddy".to_string()); myfoo.set_property("author-nick", "freddy-nick".to_value()); let author_name: String = myfoo.property("author-nick"); assert_eq!(author_name, "freddy-nick".to_string()); // read_only assert_eq!( myfoo.find_property("read_only_text").unwrap().flags(), ParamFlags::READABLE ); // custom flags assert_eq!( myfoo.find_property("custom_flags").unwrap().flags(), ParamFlags::EXPLICIT_NOTIFY | ParamFlags::READWRITE | ParamFlags::LAX_VALIDATION ); // default value assert_eq!( myfoo .find_property("with_default") .unwrap() .default_value() .get::() .unwrap(), "hello".to_string() ); // numeric builder assert_eq!( myfoo .find_property("numeric_builder") .unwrap() .downcast::() .unwrap() .maximum(), 5 ); assert_eq!( { let spec = myfoo .find_property("builder_fields_without_builder") .unwrap() .downcast::() .unwrap(); (spec.minimum(), spec.maximum()) }, (0, 5) ); // builder with required param assert_eq!( myfoo .find_property("builder_with_required_param") .unwrap() .default_value() .get::() .unwrap(), 'c' ); // boxed type assert_eq!( myfoo.property::("boxed"), foo::SimpleBoxedString("".into()) ); // Test `FooPropertiesExt` // getters { // simple let bar = myfoo.bar(); assert_eq!(bar, myfoo.property::("bar")); // custom let buzz = myfoo.buzz(); assert_eq!(buzz, myfoo.property::("buzz")); // member of struct field let author_nick = myfoo.author_nick(); assert_eq!(author_nick, myfoo.property::("author-nick")); } // setters { // simple myfoo.set_bar("setter working"); assert_eq!( myfoo.property::("bar"), "setter working".to_string() ); myfoo.set_double(0.1); assert_eq!(myfoo.property::("double"), 0.1); myfoo.set_infer_inline_type(42.0); assert_eq!(myfoo.property::("infer-inline-type"), 42.0); // simple with various String types myfoo.set_bar(String::from("setter working")); myfoo.set_bar(glib::GString::from("setter working")); assert_eq!( myfoo.property::("bar"), "setter working".to_string() ); // custom myfoo.set_fake_field("fake setter"); assert_eq!( myfoo.property::("author-name"), "fake setter".to_string() ); // member of struct field myfoo.set_author_nick("setter nick"); assert_eq!( myfoo.property::("author-nick"), "setter nick".to_string() ); } // overrides { let overridden: u32 = myfoo.property("overridden"); assert_eq!(overridden, 43); let not_overridden: u32 = myfoo.property("not-overridden"); assert_eq!(not_overridden, 42); } // optional myfoo.set_optional(Some("Hello world")); assert_eq!(myfoo.optional(), Some("Hello world".to_string())); myfoo.connect_optional_notify(|_| println!("notified")); // object subclass let myobj = glib::BoxedAnyObject::new(""); myfoo.set_object(Some(myobj.upcast_ref())); assert_eq!(myfoo.object(), Some(myobj.upcast())); // construct_only let myfoo: foo::Foo = glib::object::Object::builder() .property("construct-only-cell", 1u32) .build(); assert_eq!(myfoo.construct_only_cell(), 1u32); // construct_only with custom setter let myfoo: foo::Foo = glib::object::Object::builder() .property("construct-only-custom-setter", "foo") .build(); assert_eq!( myfoo.construct_only_custom_setter(), Some("custom set: foo".to_owned()) ); } #[test] fn ext_trait() { use base::imp::BasePropertiesExt; let base: base::Base = glib::object::Object::builder().build(); assert_eq!(BasePropertiesExt::overridden(&base), 42); let foo_obj: foo::Foo = glib::object::Object::builder().build(); assert_eq!(BasePropertiesExt::overridden(&foo_obj), 43); assert_eq!(foo_obj.overridden(), 43); } #[test] fn keyword_propnames() { mod kw_names { mod imp { use std::cell::Cell; use glib::{prelude::*, subclass::prelude::*}; use glib_macros::Properties; #[derive(Properties, Default)] #[properties(wrapper_type = super::KwNames)] pub struct KwNames { // Some of the strict keywords #[property(get, set)] r#loop: Cell, #[property(get, set)] r#move: Cell, #[property(get, set)] r#type: Cell, // Lexer 2018+ strict keywords #[property(get, set)] r#async: Cell, #[property(get, set)] r#await: Cell, #[property(get, set)] r#dyn: Cell, // Some of the reserved keywords #[property(get, set)] r#become: Cell, #[property(get, set)] r#macro: Cell, #[property(get, set)] r#unsized: Cell, // Lexer 2018+ reserved keywords #[property(get, set)] r#try: Cell, } #[glib::object_subclass] impl ObjectSubclass for KwNames { const NAME: &'static str = "MyKwNames"; type Type = super::KwNames; } #[glib::derived_properties] impl ObjectImpl for KwNames {} } glib::wrapper! { pub struct KwNames(ObjectSubclass); } } let mykwnames: kw_names::KwNames = glib::Object::new(); // make sure all 10 properties are registered assert_eq!(mykwnames.list_properties().len(), 10); // getting property values assert_eq!(mykwnames.r#loop(), 0); assert_eq!(mykwnames.r#move(), 0); assert_eq!(mykwnames.r#type(), 0); assert_eq!(mykwnames.r#async(), 0); assert_eq!(mykwnames.r#await(), 0); assert_eq!(mykwnames.r#try(), 0); // getting property by name assert_eq!(mykwnames.property::("loop"), 0); assert_eq!(mykwnames.property::("move"), 0); assert_eq!(mykwnames.property::("type"), 0); assert_eq!(mykwnames.property::("async"), 0); assert_eq!(mykwnames.property::("await"), 0); assert_eq!(mykwnames.property::("try"), 0); // setting property values mykwnames.set_loop(128_u8); assert_eq!(mykwnames.r#loop(), 128_u8); mykwnames.set_move(128_u8); assert_eq!(mykwnames.r#move(), 128_u8); mykwnames.set_type(128_u8); assert_eq!(mykwnames.r#type(), 128_u8); mykwnames.set_async(128_u8); assert_eq!(mykwnames.r#async(), 128_u8); mykwnames.set_await(128_u8); assert_eq!(mykwnames.r#await(), 128_u8); mykwnames.set_try(128_u8); assert_eq!(mykwnames.r#try(), 128_u8); // setting property by name mykwnames.set_property("loop", 255_u8); assert_eq!(mykwnames.r#loop(), 255_u8); mykwnames.set_property("move", 255_u8); assert_eq!(mykwnames.r#loop(), 255_u8); mykwnames.set_property("type", 255_u8); assert_eq!(mykwnames.r#loop(), 255_u8); mykwnames.set_property("async", 255_u8); assert_eq!(mykwnames.r#async(), 255_u8); mykwnames.set_property("await", 255_u8); assert_eq!(mykwnames.r#await(), 255_u8); mykwnames.set_property("try", 255_u8); assert_eq!(mykwnames.r#try(), 255_u8); } /// This struct is intentionally left empty. /// /// Ensure the code compiles even when no properties are specified at all. /// This is useful for refactoring. #[test] #[allow(unreachable_code)] fn empty_struct() { mod empty { mod imp { use glib::subclass::prelude::*; use glib_macros::Properties; #[derive(Properties, Default)] #[properties(wrapper_type = super::Empty)] pub struct Empty {} #[glib::object_subclass] impl ObjectSubclass for Empty { const NAME: &'static str = "Empty"; type Type = super::Empty; } #[glib::derived_properties] impl ObjectImpl for Empty {} } glib::wrapper! { pub struct Empty(ObjectSubclass); } } } glib-macros-0.20.4/tests/test.rs000064400000000000000000000576221046102023000146110ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use glib::{ collections::PtrSlice, prelude::*, translate::{FromGlib, IntoGlib}, }; #[test] fn derive_error_domain() { #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::ErrorDomain)] #[error_domain(name = "TestError")] enum TestError { Invalid, Bad, Wrong, } let err = glib::Error::new(TestError::Bad, "oh no!"); assert!(err.is::()); assert!(matches!(err.kind::(), Some(TestError::Bad))); } #[test] fn derive_shared_arc() { #[derive(Debug, Eq, PartialEq, Clone)] struct MyInnerShared { foo: String, } #[derive(Debug, Eq, PartialEq, Clone, glib::SharedBoxed)] #[shared_boxed_type(name = "MyShared")] struct MyShared(std::sync::Arc); let t = MyShared::static_type(); assert!(t.is_a(glib::Type::BOXED)); assert_eq!(t.name(), "MyShared"); let p = MyShared(std::sync::Arc::new(MyInnerShared { foo: String::from("bar"), })); assert_eq!(std::sync::Arc::strong_count(&p.0), 1); let v = p.to_value(); assert_eq!(std::sync::Arc::strong_count(&p.0), 2); let p_clone = v.get::().unwrap(); assert_eq!(std::sync::Arc::strong_count(&p.0), 3); drop(p_clone); assert_eq!(std::sync::Arc::strong_count(&p.0), 2); drop(v); assert_eq!(std::sync::Arc::strong_count(&p.0), 1); } #[test] fn derive_shared_arc_nullable() { #[derive(Debug, Eq, PartialEq, Clone)] struct MyInnerNullableShared { foo: String, } #[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)] #[shared_boxed_type(name = "MyNullableShared", nullable)] struct MyNullableShared(std::sync::Arc); let t = MyNullableShared::static_type(); assert!(t.is_a(glib::Type::BOXED)); assert_eq!(t.name(), "MyNullableShared"); let p = MyNullableShared(std::sync::Arc::new(MyInnerNullableShared { foo: String::from("bar"), })); assert_eq!(std::sync::Arc::strong_count(&p.0), 1); let _v = p.to_value(); assert_eq!(std::sync::Arc::strong_count(&p.0), 2); let p = Some(MyNullableShared(std::sync::Arc::new( MyInnerNullableShared { foo: String::from("foo"), }, ))); assert_eq!(std::sync::Arc::strong_count(&p.as_ref().unwrap().0), 1); let v = p.to_value(); assert_eq!(std::sync::Arc::strong_count(&p.as_ref().unwrap().0), 2); assert_eq!( p.as_ref().unwrap().0.foo, v.get::().unwrap().0.foo ); let b: Option<&MyNullableShared> = None; let v = b.to_value(); assert_eq!(None, v.get::>().unwrap()); } #[test] fn derive_enum() { #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::Enum)] #[repr(u32)] #[enum_type(name = "TestAnimalType")] enum Animal { Goat, #[enum_value(name = "The Dog")] Dog, #[enum_value(name = "The Cat", nick = "chat")] Cat = 5, Badger, } assert_eq!(Animal::Goat.into_glib(), 0); assert_eq!(Animal::Dog.into_glib(), 1); assert_eq!(Animal::Cat.into_glib(), 5); assert_eq!(unsafe { Animal::from_glib(0) }, Animal::Goat); assert_eq!(unsafe { Animal::from_glib(1) }, Animal::Dog); assert_eq!(unsafe { Animal::from_glib(5) }, Animal::Cat); assert_eq!(Animal::Goat.to_value().get::(), Ok(Animal::Goat)); assert_eq!(Animal::Dog.to_value().get::(), Ok(Animal::Dog)); assert_eq!(Animal::Cat.to_value().get::(), Ok(Animal::Cat)); let t = Animal::static_type(); assert!(t.is_a(glib::Type::ENUM)); assert_eq!(t.name(), "TestAnimalType"); let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); let v = e.value(0).expect("EnumClass::get_value(0) failed"); assert_eq!(v.name(), "Goat"); assert_eq!(v.nick(), "goat"); let v = e.value(1).expect("EnumClass::get_value(1) failed"); assert_eq!(v.name(), "The Dog"); assert_eq!(v.nick(), "dog"); let v = e.value(5).expect("EnumClass::get_value(5) failed"); assert_eq!(v.name(), "The Cat"); assert_eq!(v.nick(), "chat"); assert_eq!(e.value(2), None); } #[test] fn derive_boxed() { #[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)] #[boxed_type(name = "MyBoxed")] struct MyBoxed(String); let t = MyBoxed::static_type(); assert!(t.is_a(glib::Type::BOXED)); assert_eq!(t.name(), "MyBoxed"); let b = MyBoxed(String::from("abc")); let v = b.to_value(); assert_eq!(&b, v.get::<&MyBoxed>().unwrap()); assert_eq!(b, v.get::().unwrap()); } #[allow(clippy::unnecessary_literal_unwrap)] #[test] fn derive_boxed_nullable() { #[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)] #[boxed_type(name = "MyNullableBoxed", nullable)] struct MyNullableBoxed(String); let t = MyNullableBoxed::static_type(); assert!(t.is_a(glib::Type::BOXED)); assert_eq!(t.name(), "MyNullableBoxed"); let b = MyNullableBoxed(String::from("abc")); let v = b.to_value(); assert_eq!(&b, v.get::>().unwrap().unwrap()); assert_eq!(b, v.get::>().unwrap().unwrap()); let b = Some(MyNullableBoxed(String::from("def"))); let v = b.to_value(); let b = b.unwrap(); assert_eq!(&b, v.get::>().unwrap().unwrap()); assert_eq!(b, v.get::>().unwrap().unwrap()); let b = Some(MyNullableBoxed(String::from("def"))); let v = b.to_value(); let b = b.unwrap(); assert_eq!(&b, v.get::>().unwrap().unwrap()); assert_eq!(b, v.get::>().unwrap().unwrap()); let b: Option = None; let v = b.to_value(); assert_eq!(None, v.get::>().unwrap()); assert_eq!(None, v.get::>().unwrap()); } #[test] fn boxed_transparent_ptr() { #[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)] #[boxed_type(name = "MyBoxed")] struct MyBoxed(String); let vec = vec![MyBoxed(String::from("abc")), MyBoxed(String::from("dfg"))]; // PtrSlice requires TransparentPtrType trait let ptr_slice = PtrSlice::from(vec); assert_eq!( ptr_slice.last(), Some(MyBoxed(String::from("dfg"))).as_ref() ); } #[test] fn attr_flags() { #[glib::flags(name = "MyFlags")] enum MyFlags { #[flags_value(name = "Flag A", nick = "nick-a")] A = 0b00000001, #[flags_value(name = "Flag B")] B = 0b00000010, #[flags_value(skip)] AB = Self::A.bits() | Self::B.bits(), C = 0b00000100, } assert_eq!(MyFlags::A.bits(), 1); assert_eq!(MyFlags::B.bits(), 2); assert_eq!(MyFlags::AB.bits(), 3); assert_eq!(MyFlags::empty().into_glib(), 0); assert_eq!(MyFlags::A.into_glib(), 1); assert_eq!(MyFlags::B.into_glib(), 2); assert_eq!(MyFlags::AB.into_glib(), 3); assert_eq!(unsafe { MyFlags::from_glib(0) }, MyFlags::empty()); assert_eq!(unsafe { MyFlags::from_glib(1) }, MyFlags::A); assert_eq!(unsafe { MyFlags::from_glib(2) }, MyFlags::B); assert_eq!(unsafe { MyFlags::from_glib(3) }, MyFlags::AB); assert_eq!( MyFlags::empty().to_value().get::(), Ok(MyFlags::empty()) ); assert_eq!(MyFlags::A.to_value().get::(), Ok(MyFlags::A)); assert_eq!(MyFlags::B.to_value().get::(), Ok(MyFlags::B)); assert_eq!(MyFlags::AB.to_value().get::(), Ok(MyFlags::AB)); let t = MyFlags::static_type(); assert!(t.is_a(glib::Type::FLAGS)); assert_eq!(t.name(), "MyFlags"); let e = glib::FlagsClass::with_type(t).expect("FlagsClass::new failed"); let v = e.value(1).expect("FlagsClass::get_value(1) failed"); assert_eq!(v.name(), "Flag A"); assert_eq!(v.nick(), "nick-a"); let v = e.value(2).expect("FlagsClass::get_value(2) failed"); assert_eq!(v.name(), "Flag B"); assert_eq!(v.nick(), "b"); let v = e.value(4).expect("FlagsClass::get_value(4) failed"); assert_eq!(v.name(), "C"); assert_eq!(v.nick(), "c"); assert!(e.value_by_name("Flag A").is_some()); assert!(e.value_by_name("Flag B").is_some()); assert!(e.value_by_name("AB").is_none()); assert!(e.value_by_name("C").is_some()); assert!(e.value_by_nick("nick-a").is_some()); assert!(e.value_by_nick("b").is_some()); assert!(e.value_by_nick("ab").is_none()); assert!(e.value_by_nick("c").is_some()); } #[test] fn attr_flags_with_default() { #[glib::flags(name = "MyFlags")] enum MyFlags { #[flags_value(name = "Flag A", nick = "nick-a")] A = 0b00000001, #[default] #[flags_value(name = "Flag B")] B = 0b00000010, } assert_eq!(MyFlags::A.bits(), 1); assert_eq!(MyFlags::B.bits(), 2); assert_eq!(MyFlags::default(), MyFlags::B); assert_eq!(MyFlags::default().into_glib(), 2); } #[test] fn subclassable() { mod foo { use glib::subclass::prelude::*; use super::*; mod imp { use super::*; #[derive(Default)] pub struct Foo {} #[glib::object_subclass] impl ObjectSubclass for Foo { const NAME: &'static str = "MyFoo"; type Type = super::Foo; } impl ObjectImpl for Foo {} impl Foo { pub(super) fn test(&self) {} } } pub trait FooExt: IsA + 'static { fn test(&self) { let imp = self.as_ref().upcast_ref::().imp(); imp.test(); } } impl> FooExt for O {} glib::wrapper! { pub struct Foo(ObjectSubclass); } } use foo::FooExt; let obj = glib::Object::new::(); obj.test(); } #[test] fn derive_variant() { #[derive(Debug, PartialEq, Eq, glib::Variant)] struct Variant1 { some_string: String, some_int: i32, } assert_eq!(Variant1::static_variant_type().as_str(), "(si)"); let v = Variant1 { some_string: String::from("bar"), some_int: 2, }; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "(si)"); assert_eq!(var.get::(), Some(v)); #[derive(Debug, PartialEq, Eq, glib::Variant)] struct Variant2 { some_string: Option, some_int: i32, } assert_eq!(Variant2::static_variant_type().as_str(), "(msi)"); let v = Variant2 { some_string: Some(String::from("bar")), some_int: 2, }; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "(msi)"); assert_eq!(var.get::(), Some(v)); #[derive(Debug, PartialEq, Eq, glib::Variant)] struct Variant3(u32, String); assert_eq!(Variant3::static_variant_type().as_str(), "(us)"); let v = Variant3(1, String::from("foo")); let var = v.to_variant(); assert_eq!(var.type_().as_str(), "(us)"); assert_eq!(var.get::(), Some(v)); #[derive(Debug, PartialEq, Eq, glib::Variant)] struct Variant4; assert_eq!(Variant4::static_variant_type().as_str(), "()"); let v = Variant4; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "()"); assert_eq!(var.get::(), Some(v)); #[derive(Debug, PartialEq, Eq, glib::Variant)] struct Variant5(); assert_eq!(Variant5::static_variant_type().as_str(), "()"); let v = Variant5(); let var = v.to_variant(); assert_eq!(var.type_().as_str(), "()"); assert_eq!(var.get::(), Some(v)); #[derive(Debug, PartialEq, Eq, glib::Variant)] enum Variant6 { Unit, Tuple(i32, Variant1), Struct { id: i64, data: Variant2 }, } assert_eq!(Variant6::static_variant_type().as_str(), "(sv)"); let v = Variant6::Unit; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "(sv)"); assert_eq!(var.get::(), Some(v)); let v = Variant6::Tuple( 5, Variant1 { some_string: "abc".into(), some_int: 77, }, ); let var = v.to_variant(); assert_eq!(var.type_().as_str(), "(sv)"); assert_eq!(var.get::(), Some(v)); let v = Variant6::Struct { id: 299, data: Variant2 { some_string: Some("abcdef".into()), some_int: 300, }, }; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "(sv)"); assert_eq!(var.get::(), Some(v)); #[derive(Debug, PartialEq, Eq, glib::Variant)] #[variant_enum(repr)] #[repr(u32)] enum Variant7 { Unit, Tuple(i32, String), Struct { id: i64, data: Vec }, } assert_eq!(Variant7::static_variant_type().as_str(), "(uv)"); let v = Variant7::Struct { id: 299, data: vec![55, 56, 57, 58], }; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "(uv)"); assert_eq!(var.get::(), Some(v)); #[derive(Debug, PartialEq, Eq, Clone, Copy, glib::Variant, glib::Enum)] #[variant_enum(enum)] #[repr(i32)] #[enum_type(name = "Variant8")] enum Variant8 { Goat, #[enum_value(name = "The Dog")] Dog, #[enum_value(name = "The Cat", nick = "chat")] Cat = 5, Badger, } assert_eq!(Variant8::static_variant_type().as_str(), "s"); let v = Variant8::Cat; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "s"); assert_eq!(var.to_string(), "'chat'"); assert_eq!(var.get::(), Some(v)); #[derive(Debug, PartialEq, Eq, Clone, Copy, glib::Variant, glib::Enum)] #[variant_enum(enum, repr)] #[repr(i32)] #[enum_type(name = "Variant9")] enum Variant9 { Goat, #[enum_value(name = "The Dog")] Dog, #[enum_value(name = "The Cat", nick = "chat")] Cat = 5, Badger, } assert_eq!(Variant9::static_variant_type().as_str(), "i"); let v = Variant9::Badger; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "i"); assert_eq!(var.get::(), Some(v)); #[derive(glib::Variant)] #[variant_enum(flags)] #[glib::flags(name = "Variant10")] enum Variant10 { EMPTY = 0, #[flags_value(name = "Flag A", nick = "nick-a")] A = 0b00000001, #[flags_value(name = "Flag B")] B = 0b00000010, #[flags_value(skip)] AB = Self::A.bits() | Self::B.bits(), C = 0b00000100, } assert_eq!(Variant10::static_variant_type().as_str(), "s"); let v = Variant10::AB; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "s"); assert_eq!(var.to_string(), "'nick-a|b'"); assert_eq!(var.get::(), Some(v)); #[derive(glib::Variant)] #[variant_enum(flags, repr)] #[glib::flags(name = "Variant11")] enum Variant11 { #[flags_value(name = "Flag A", nick = "nick-a")] A = 0b00000001, #[flags_value(name = "Flag B")] B = 0b00000010, #[flags_value(skip)] AB = Self::A.bits() | Self::B.bits(), C = 0b00000100, } assert_eq!(Variant11::static_variant_type().as_str(), "u"); let v = Variant11::A | Variant11::C; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "u"); assert_eq!(var.get::(), Some(v)); #[derive(Debug, PartialEq, Eq, glib::Variant)] enum Variant12 { Goat, Dog, Cat = 5, Badger, } assert_eq!(Variant12::static_variant_type().as_str(), "s"); let v = Variant12::Dog; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "s"); assert_eq!(var.get::(), Some(v)); #[derive(Debug, PartialEq, Eq, Copy, Clone, glib::Variant)] #[variant_enum(repr)] #[repr(u8)] enum Variant13 { Goat, Dog, Cat = 5, Badger, } assert_eq!(Variant13::static_variant_type().as_str(), "y"); let v = Variant13::Badger; let var = v.to_variant(); assert_eq!(var.type_().as_str(), "y"); assert_eq!(var.get::(), Some(v)); } #[test] fn closure() { let empty = glib::closure!(|| {}); empty.invoke::<()>(&[]); let no_arg = glib::closure!(|| 2i32); assert_eq!(no_arg.invoke::(&[]), 2); let add_1 = glib::closure!(|x: i32| x + 1); assert_eq!(add_1.invoke::(&[&3i32]), 4); let concat_str = glib::closure!(|s: &str| s.to_owned() + " World"); assert_eq!(concat_str.invoke::(&[&"Hello"]), "Hello World"); let weak_test = { let obj = glib::Object::new::(); assert_eq!(obj.ref_count(), 1); let weak_test = glib::closure_local!( #[watch] obj, move || obj.ref_count() ); assert_eq!(obj.ref_count(), 1); assert_eq!(weak_test.invoke::(&[]), 2); assert_eq!(obj.ref_count(), 1); weak_test }; weak_test.invoke::<()>(&[]); { trait TestExt { fn ref_count_in_closure(&self) -> u32; } impl TestExt for glib::Object { fn ref_count_in_closure(&self) -> u32 { let closure = glib::closure_local!( #[watch(rename_to = obj)] self, move || obj.ref_count() ); closure.invoke::(&[]) } } let obj = glib::Object::new::(); assert_eq!(obj.ref_count_in_closure(), 2); } { struct A { obj: glib::Object, } impl A { fn ref_count_in_closure(&self) -> u32 { let closure = glib::closure_local!( #[watch(rename_to = obj)] self.obj, move || obj.ref_count() ); closure.invoke::(&[]) } } let a = A { obj: glib::Object::new::(), }; assert_eq!(a.ref_count_in_closure(), 2); } let strong_test = { let obj = glib::Object::new::(); let strong_test = glib::closure_local!( #[strong] obj, move || obj.ref_count() ); assert_eq!(strong_test.invoke::(&[]), 2); strong_test }; assert_eq!(strong_test.invoke::(&[]), 1); let weak_none_test = { let obj = glib::Object::new::(); let weak_none_test = glib::closure_local!( #[weak_allow_none] obj, move || { obj.map(|o| o.ref_count()).unwrap_or_default() } ); assert_eq!(weak_none_test.invoke::(&[]), 2); weak_none_test }; assert_eq!(weak_none_test.invoke::(&[]), 0); let weak_test_or_else = { let obj = glib::Object::new::(); let weak_test = glib::closure_local!( #[weak] obj, #[upgrade_or_else] || 0, move || obj.ref_count() ); assert_eq!(weak_test.invoke::(&[]), 2); weak_test }; assert_eq!(weak_test_or_else.invoke::(&[]), 0); let weak_test_or = { let obj = glib::Object::new::(); let weak_test = glib::closure_local!( #[weak] obj, #[upgrade_or] 0, move || obj.ref_count() ); assert_eq!(weak_test.invoke::(&[]), 2); weak_test }; assert_eq!(weak_test_or.invoke::(&[]), 0); let weak_test_or_default = { let obj = glib::Object::new::(); let weak_test = glib::closure_local!( #[weak] obj, #[upgrade_or_default] move || obj.ref_count() ); assert_eq!(weak_test.invoke::(&[]), 2); weak_test }; assert_eq!(weak_test_or_default.invoke::(&[]), 0); { let ret = std::rc::Rc::new(std::cell::Cell::new(0)); let weak_test_or_unit = { let obj = glib::Object::new::(); let weak_test = glib::closure_local!( #[weak] obj, #[strong] ret, move || { ret.set(obj.ref_count()); } ); weak_test.invoke::<()>(&[]); assert_eq!(ret.get(), 2); ret.set(0); weak_test }; weak_test_or_unit.invoke::<()>(&[]); assert_eq!(ret.get(), 0); } { let obj1 = glib::Object::new::(); let obj2 = glib::Object::new::(); let obj_arg_test = glib::closure!(|a: glib::Object, b: glib::Object| { a.ref_count() + b.ref_count() }); let rc = obj_arg_test.invoke::(&[&obj1, &obj2]); assert_eq!(rc, 6); let alias_test = glib::closure_local!( #[strong(rename_to = a)] obj1, #[strong] obj2, move || { a.ref_count() + obj2.ref_count() } ); assert_eq!(alias_test.invoke::(&[]), 4); } { struct A { a: glib::Object, } let a = glib::Object::new::(); let a_struct = A { a }; let struct_test = glib::closure_local!( #[strong(rename_to = a)] a_struct.a, move || { a.ref_count() } ); assert_eq!(struct_test.invoke::(&[]), 2); } { use glib::{prelude::*, subclass::prelude::*}; #[derive(Default)] pub struct FooPrivate {} #[glib::object_subclass] impl ObjectSubclass for FooPrivate { const NAME: &'static str = "MyFoo2"; type Type = Foo; } impl ObjectImpl for FooPrivate {} glib::wrapper! { pub struct Foo(ObjectSubclass); } impl Foo { fn my_ref_count(&self) -> u32 { self.ref_count() } } let cast_test = { let f = glib::Object::new::(); assert_eq!(f.my_ref_count(), 1); let cast_test = glib::closure_local!( #[watch] f, move || f.my_ref_count() ); assert_eq!(f.my_ref_count(), 1); assert_eq!(cast_test.invoke::(&[]), 2); assert_eq!(f.my_ref_count(), 1); let f_ref = &f; let _ = glib::closure_local!( #[watch] f_ref, move || f_ref.my_ref_count() ); cast_test }; cast_test.invoke::<()>(&[]); } { use glib::subclass::prelude::*; #[derive(Default)] pub struct SendObjectPrivate { value: std::sync::Mutex, } #[glib::object_subclass] impl ObjectSubclass for SendObjectPrivate { const NAME: &'static str = "SendObject"; type Type = SendObject; } impl ObjectImpl for SendObjectPrivate {} glib::wrapper! { pub struct SendObject(ObjectSubclass); } impl SendObject { fn value(&self) -> i32 { *self.imp().value.lock().unwrap() } fn set_value(&self, v: i32) { *self.imp().value.lock().unwrap() = v; } } let inc_by = { let obj = glib::Object::new::(); let obj = obj.imp().obj(); let inc_by = glib::closure!( #[watch] obj, move |x: i32| { let old = obj.value(); obj.set_value(x + old); old } ); obj.set_value(42); assert_eq!(obj.value(), 42); assert_eq!(inc_by.invoke::(&[&24i32]), 42); assert_eq!(obj.value(), 66); inc_by }; inc_by.invoke::<()>(&[]); } } glib-macros-0.20.4/tests/value_delegate_derive.rs000064400000000000000000000205131046102023000201230ustar 00000000000000use glib::{prelude::*, value::FromValue, Value, ValueDelegate}; #[test] fn into_value() { fn test_func(_: impl Into) {} #[derive(ValueDelegate)] pub struct Test(i32); #[derive(ValueDelegate)] #[value_delegate(nullable)] pub struct TestNullable(String); #[derive(ValueDelegate)] #[value_delegate(from = i64)] pub struct TestManualFrom(i32); impl From for TestManualFrom { fn from(v: i64) -> Self { Self(v as i32) } } impl<'a> From<&'a TestManualFrom> for i64 { fn from(v: &'a TestManualFrom) -> Self { v.0 as i64 } } impl From for i64 { fn from(v: TestManualFrom) -> Self { v.0 as i64 } } test_func(Test(123)); test_func(Test(123)); test_func(TestManualFrom(123)); test_func(TestManualFrom(123)); test_func(TestNullable("foo".to_string())); test_func(TestNullable("foo".to_string())); test_func(Some(&TestNullable("foo".to_string()))); test_func(Some(TestNullable("foo".to_string()))); test_func(Some(TestNullable("foo".to_string()))); assert_eq!(glib::Value::from(Test(123)).get::().unwrap().0, 123); assert_eq!(glib::Value::from(123).get::().unwrap().0, 123); assert_eq!(glib::Value::from(Test(123)).get::().unwrap(), 123); assert_eq!( glib::Value::from(TestManualFrom(123)) .get::() .unwrap() .0, 123 ); assert_eq!( glib::Value::from(123_i64) .get::() .unwrap() .0, 123 ); assert_eq!( glib::Value::from(TestManualFrom(123)).get::().unwrap(), 123 ); // From TestNullable assert_eq!( glib::Value::from(TestNullable("foo".to_string())) .get::>() .unwrap() .unwrap() .0, "foo" ); assert_eq!( glib::Value::from("foo") .get::>() .unwrap() .unwrap() .0, "foo" ); assert_eq!( glib::Value::from(TestNullable("foo".to_string())) .get::>() .unwrap() .unwrap(), "foo" ); // From Option Some assert_eq!( glib::Value::from(Some(TestNullable("foo".to_string()))) .get::>() .unwrap() .unwrap() .0, "foo" ); assert_eq!( glib::Value::from(Some("foo")) .get::>() .unwrap() .unwrap() .0, "foo" ); assert_eq!( glib::Value::from(Some(TestNullable("foo".to_string()))) .get::>() .unwrap() .unwrap(), "foo" ); // From Option None assert!(glib::Value::from(None::) .get::>() .unwrap() .is_none()); assert!(glib::Value::from(None::) .get::>() .unwrap() .is_none()); assert!(glib::Value::from(None::) .get::>() .unwrap() .is_none()); } #[allow(clippy::unnecessary_literal_unwrap)] #[test] fn higher_level_types() { #[derive(Debug, ValueDelegate)] pub struct MyVec(Vec); #[derive(Debug, ValueDelegate)] #[value_delegate(nullable)] pub struct MyString(Box); #[derive(Debug, ValueDelegate)] #[value_delegate(from = Option)] struct MyVecManualFrom(Vec); impl From> for MyVecManualFrom { fn from(v: Option) -> Self { Self(v.into_iter().collect::>()) } } impl<'a> From<&'a MyVecManualFrom> for Option { fn from(v: &'a MyVecManualFrom) -> Self { v.0.first().cloned() } } impl From for Option { fn from(v: MyVecManualFrom) -> Self { v.0.into_iter().next() } } let vec = vec!["foo".to_string(), "bar".to_string()]; let vec_value = vec.to_value(); let my_vec_value = MyVec(vec).to_value(); assert_eq!(MyVec::static_type(), Vec::::static_type()); assert_eq!( vec_value.get::>().unwrap(), my_vec_value.get::>().unwrap(), ); assert_eq!(vec_value.value_type(), my_vec_value.value_type()); assert_eq!(unsafe { Vec::::from_value(&vec_value) }, unsafe { MyVec::from_value(&vec_value).0 }); assert_eq!( unsafe { Vec::::from_value(&my_vec_value) }, unsafe { MyVec::from_value(&my_vec_value).0 } ); let string = "foo".to_string(); let string_value = string.to_value(); let my_string_value = MyString(string.into()).to_value(); assert_eq!(MyString::static_type(), Box::::static_type()); assert_eq!( string_value.get::>().unwrap(), my_string_value.get::>().unwrap(), ); assert_eq!(string_value.value_type(), my_string_value.value_type()); assert_eq!(unsafe { Box::::from_value(&string_value) }, unsafe { MyString::from_value(&string_value).0 }); assert_eq!( unsafe { Box::::from_value(&my_string_value) }, unsafe { MyString::from_value(&my_string_value).0 } ); let string_some = Some("foo".to_string()); let string_some_value = string_some.to_value(); let string_none_value = None::.to_value(); let my_string_some_value = MyString(string_some.unwrap().into()).to_value(); let my_string_none_value = None::.to_value(); assert_eq!( Option::::static_type(), Option::>::static_type() ); assert_eq!( string_some_value .get::>>() .unwrap() .unwrap(), my_string_some_value .get::>>() .unwrap() .unwrap(), ); assert_eq!( string_none_value .get::>>() .unwrap() .is_none(), my_string_none_value .get::>>() .unwrap() .is_none(), ); assert_eq!( string_some_value.value_type(), my_string_some_value.value_type() ); assert_eq!( string_none_value.value_type(), my_string_none_value.value_type() ); assert_eq!( unsafe { Option::>::from_value(&string_some_value).unwrap() }, unsafe { Option::::from_value(&string_some_value) .unwrap() .0 } ); assert_eq!( unsafe { Option::>::from_value(&string_none_value).is_none() }, unsafe { Option::::from_value(&string_none_value).is_none() } ); assert_eq!( unsafe { Option::>::from_value(&my_string_some_value).unwrap() }, unsafe { Option::::from_value(&my_string_some_value) .unwrap() .0 } ); assert_eq!( unsafe { Option::>::from_value(&my_string_none_value).is_none() }, unsafe { Option::::from_value(&my_string_none_value).is_none() } ); let opt = Some("foo".to_string()); let opt_value = opt.to_value(); let my_vec_manual_from_value = MyVecManualFrom::from(opt).to_value(); assert_eq!( MyVecManualFrom::static_type(), Option::::static_type() ); assert_eq!( opt_value.get::>().unwrap(), my_vec_manual_from_value.get::>().unwrap(), ); assert_eq!( opt_value.value_type(), my_vec_manual_from_value.value_type() ); assert_eq!( unsafe { Option::::from_value(&opt_value) .into_iter() .collect::>() }, unsafe { MyVecManualFrom::from_value(&opt_value).0 } ); assert_eq!( unsafe { Option::::from_value(&my_vec_manual_from_value) .into_iter() .collect::>() }, unsafe { MyVecManualFrom::from_value(&my_vec_manual_from_value).0 } ); }