pax_global_header00006660000000000000000000000064146745407720014532gustar00rootroot0000000000000052 comment=3307da75b58f852e87892a201aad3d841013e4c3 rust-glib-macros-0.18-0.18.2/000077500000000000000000000000001467454077200154005ustar00rootroot00000000000000rust-glib-macros-0.18-0.18.2/.cargo_vcs_info.json000066400000000000000000000001511467454077200213270ustar00rootroot00000000000000{ "git": { "sha1": "1ce51ac2eb3e11c34088a7249a17e767e63ee0d9" }, "path_in_vcs": "glib-macros" }rust-glib-macros-0.18-0.18.2/COPYRIGHT000066400000000000000000000012131467454077200166700ustar00rootroot00000000000000The 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. rust-glib-macros-0.18-0.18.2/Cargo.toml000066400000000000000000000025271467454077200173360ustar00rootroot00000000000000# 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.18.2" authors = ["The gtk-rs Project Developers"] 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/" keywords = [ "glib", "gtk-rs", "gnome", "GUI", ] license = "MIT" repository = "https://github.com/gtk-rs/gtk-rs-core" resolver = "1" [lib] proc-macro = true [dependencies.heck] version = "0.4" [dependencies.proc-macro-crate] version = "1.0" [dependencies.proc-macro-error] version = "1.0" [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "2.0" features = ["full"] [dev-dependencies.glib] version = "0.18" [dev-dependencies.once_cell] version = "1.9.0" [dev-dependencies.trybuild2] version = "1.1" rust-glib-macros-0.18-0.18.2/Cargo.toml.orig000066400000000000000000000013211467454077200202640ustar00rootroot00000000000000[package] name = "glib-macros" documentation = "https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib_macros/" homepage = "https://gtk-rs.org/" authors = ["The gtk-rs Project Developers"] description = "Rust bindings for the GLib library, proc macros crate" version = "0.18.2" keywords = ["glib", "gtk-rs", "gnome", "GUI"] repository = "https://github.com/gtk-rs/gtk-rs-core" license = "MIT" edition = "2021" rust-version = "1.70" [dependencies] heck = "0.4" proc-macro-error = "1.0" proc-macro2 = "1.0" quote = "1.0" syn = { version = "2.0", features = ["full"] } proc-macro-crate = "1.0" [lib] proc-macro = true [dev-dependencies] glib = { path = "../glib", version = "0.18" } trybuild2 = "1.1" once_cell = "1.9.0" rust-glib-macros-0.18-0.18.2/LICENSE000066400000000000000000000020001467454077200163750ustar00rootroot00000000000000Permission 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. rust-glib-macros-0.18-0.18.2/src/000077500000000000000000000000001467454077200161675ustar00rootroot00000000000000rust-glib-macros-0.18-0.18.2/src/boxed_derive.rs000066400000000000000000000244301467454077200211770ustar00rootroot00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::{Ident, TokenStream}; use proc_macro_error::abort_call_site; 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) -> TokenStream { let name = &input.ident; let mut gtype_name = NestedMetaItem::::new("name") .required() .value_required(); let mut nullable = NestedMetaItem::::new("nullable").value_optional(); let found = parse_nested_meta_items( &input.attrs, "boxed_type", &mut [&mut gtype_name, &mut nullable], ); match found { Ok(None) => { abort_call_site!( "#[derive(glib::Boxed)] requires #[boxed_type(name = \"BoxedTypeName\")]" ) } Err(e) => return e.to_compile_error(), Ok(_) => {} }; let gtype_name = gtype_name.value.unwrap(); let nullable = nullable.found || nullable.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! {} }; quote! { impl #crate_ident::subclass::boxed::BoxedType for #name { const NAME: &'static ::core::primitive::str = #gtype_name; } impl #crate_ident::StaticType for #name { #[inline] fn static_type() -> #crate_ident::Type { static ONCE: ::std::sync::Once = ::std::sync::Once::new(); static mut TYPE_: #crate_ident::Type = #crate_ident::Type::INVALID; ONCE.call_once(|| { let type_ = #crate_ident::subclass::register_boxed_type::<#name>(); unsafe { TYPE_ = type_; } }); unsafe { TYPE_ } } } 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::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::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::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 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::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) } } } } rust-glib-macros-0.18-0.18.2/src/clone.rs000066400000000000000000000751761467454077200176550ustar00rootroot00000000000000// 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) } pub 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(); 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) } rust-glib-macros-0.18-0.18.2/src/closure.rs000066400000000000000000000220111467454077200202050ustar00rootroot00000000000000// 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_error::abort; 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("-"); Ok(match keyword.as_str() { "strong" => CaptureKind::Strong, "watch" => CaptureKind::Watch, "weak-allow-none" => CaptureKind::WeakAllowNone, "to-owned" => CaptureKind::ToOwned, k => abort!( idents, "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" { abort!( 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('.') { abort!( name.span(), "`{}`: 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) { abort!( capture.start, "Only one `@watch` capture is allowed per closure"; note = existing.start => "Previous `@watch` found here" ); } } 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() { abort!(closure, "Async closure not allowed"); } if !captures.is_empty() && closure.capture.is_none() { abort!( 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()); 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(), ); #(#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() } rust-glib-macros-0.18-0.18.2/src/derived_properties_attribute.rs000066400000000000000000000040151467454077200245160ustar00rootroot00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; 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) -> TokenStream { let syn::ItemImpl { attrs, generics, trait_, self_ty, items, .. } = input; let trait_path = match &trait_ { Some(path) => &path.1, None => abort_call_site!(WRONG_PLACE_MSG), }; 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), ]; quote!( #(#attrs)* impl #generics #trait_path for #self_ty { #(#items)* #(#generated)* } ) } rust-glib-macros-0.18-0.18.2/src/downgrade_derive/000077500000000000000000000000001467454077200214775ustar00rootroot00000000000000rust-glib-macros-0.18-0.18.2/src/downgrade_derive/enums.rs000066400000000000000000000077161467454077200232070ustar00rootroot00000000000000// 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() } rust-glib-macros-0.18-0.18.2/src/downgrade_derive/fields.rs000066400000000000000000000131761467454077200233230ustar00rootroot00000000000000// 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 { /// Choise::This (ref _0, ref _1) => ... , /// Choise::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! {}, }, } } rust-glib-macros-0.18-0.18.2/src/downgrade_derive/mod.rs000066400000000000000000000012121467454077200226200ustar00rootroot00000000000000// 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."); } } } rust-glib-macros-0.18-0.18.2/src/downgrade_derive/structs.rs000066400000000000000000000063771467454077200235710ustar00rootroot00000000000000// 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() } rust-glib-macros-0.18-0.18.2/src/enum_derive.rs000066400000000000000000000160561467454077200210470ustar00rootroot00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use heck::{ToKebabCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; use quote::{quote, quote_spanned}; use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, Data, Ident, Variant}; use crate::utils::{crate_ident_new, gen_enum_from_glib, parse_nested_meta_items, NestedMetaItem}; // Generate 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: &Ident, enum_variants: &Punctuated, ) -> (TokenStream, usize) { let crate_ident = crate_ident_new(); // start 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; quote_spanned! {v.span()=> #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) -> TokenStream { let name = &input.ident; let enum_variants = match input.data { Data::Enum(ref e) => &e.variants, _ => abort_call_site!("#[derive(glib::Enum)] only supports enums"), }; let mut gtype_name = NestedMetaItem::::new("name") .required() .value_required(); let found = parse_nested_meta_items(&input.attrs, "enum_type", &mut [&mut gtype_name]); match found { Ok(None) => { abort_call_site!("#[derive(glib::Enum)] requires #[enum_type(name = \"EnumTypeName\")]") } Err(e) => return e.to_compile_error(), Ok(attr) => attr, }; let gtype_name = gtype_name.value.unwrap(); let from_glib = gen_enum_from_glib(name, enum_variants); let (enum_values, nb_enum_values) = gen_enum_values(name, enum_variants); let crate_ident = crate_ident_new(); 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::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_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::StaticType for #name { #[inline] fn static_type() -> #crate_ident::Type { static ONCE: ::std::sync::Once = ::std::sync::Once::new(); static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID; ONCE.call_once(|| { static mut VALUES: [#crate_ident::gobject_ffi::GEnumValue; #nb_enum_values] = [ #enum_values #crate_ident::gobject_ffi::GEnumValue { value: 0, value_name: ::std::ptr::null(), value_nick: ::std::ptr::null(), }, ]; let name = ::std::ffi::CString::new(#gtype_name).expect("CString::new failed"); unsafe { let type_ = #crate_ident::gobject_ffi::g_enum_register_static(name.as_ptr(), VALUES.as_ptr()); let type_: #crate_ident::Type = #crate_ident::translate::from_glib(type_); assert!(type_.is_valid()); TYPE = type_; } }); unsafe { TYPE } } } 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) } } } } rust-glib-macros-0.18-0.18.2/src/error_domain_derive.rs000066400000000000000000000040471467454077200225600ustar00rootroot00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; 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) -> TokenStream { let name = &input.ident; let enum_variants = match input.data { Data::Enum(ref e) => &e.variants, _ => abort_call_site!("#[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]); match found { Ok(None) => { abort_call_site!( "#[derive(glib::ErrorDomain)] requires #[error_domain(name = \"domain-name\")]" ) } Err(e) => return e.to_compile_error(), Ok(_) => (), }; let domain_name = domain_name.value.unwrap(); let crate_ident = crate_ident_new(); let from_glib = gen_enum_from_glib(name, enum_variants); quote! { impl #crate_ident::error::ErrorDomain for #name { #[inline] fn domain() -> #crate_ident::Quark { use #crate_ident::translate::from_glib; static QUARK: #crate_ident::once_cell::sync::Lazy<#crate_ident::Quark> = #crate_ident::once_cell::sync::Lazy::new(|| unsafe { from_glib(#crate_ident::ffi::g_quark_from_static_string(concat!(#domain_name, "\0") as *const ::core::primitive::str as *const _)) }); *QUARK } #[inline] fn code(self) -> i32 { self as i32 } #[inline] fn from(value: i32) -> ::core::option::Option where Self: ::std::marker::Sized { #from_glib } } } } rust-glib-macros-0.18-0.18.2/src/flags_attribute.rs000066400000000000000000000172021467454077200217160ustar00rootroot00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use heck::{ToKebabCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; use quote::{quote, quote_spanned}; use syn::{ punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DeriveInput, Ident, Variant, Visibility, }; use crate::utils::{crate_ident_new, parse_nested_meta_items, NestedMetaItem}; pub struct AttrInput { pub enum_name: syn::LitStr, } 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)* } } } } pub fn impl_flags(attrs: AttrInput, input: &DeriveInput) -> TokenStream { let gtype_name = attrs.enum_name.value(); let name = &input.ident; let visibility = &input.vis; let enum_variants = match input.data { Data::Enum(ref e) => &e.variants, _ => abort_call_site!("#[glib::flags] only supports enums"), }; let crate_ident = crate_ident_new(); let bitflags = gen_bitflags(name, visibility, enum_variants, &crate_ident); let (flags_values, nb_flags_values) = gen_flags_values(name, enum_variants); quote! { #bitflags 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::StaticType for #name { #[inline] fn static_type() -> #crate_ident::Type { static ONCE: ::std::sync::Once = ::std::sync::Once::new(); static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID; ONCE.call_once(|| { static mut VALUES: [#crate_ident::gobject_ffi::GFlagsValue; #nb_flags_values] = [ #flags_values #crate_ident::gobject_ffi::GFlagsValue { value: 0, value_name: ::std::ptr::null(), value_nick: ::std::ptr::null(), }, ]; let name = ::std::ffi::CString::new(#gtype_name).expect("CString::new failed"); unsafe { let type_ = #crate_ident::gobject_ffi::g_flags_register_static(name.as_ptr(), VALUES.as_ptr()); let type_: #crate_ident::Type = #crate_ident::translate::from_glib(type_); assert!(type_.is_valid()); TYPE = type_; } }); unsafe { TYPE } } } } } rust-glib-macros-0.18-0.18.2/src/lib.rs000066400000000000000000001122551467454077200173110ustar00rootroot00000000000000// Take a look at the license at the top of the repository in the LICENSE file. mod boxed_derive; mod clone; mod closure; mod derived_properties_attribute; mod downgrade_derive; mod enum_derive; mod error_domain_derive; mod flags_attribute; mod object_interface_attribute; mod object_subclass_attribute; mod properties; mod shared_boxed_derive; mod value_delegate_derive; mod variant_derive; mod utils; use flags_attribute::AttrInput; use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; 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`: /// /// ``` /// 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 => @default-return false, 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 v as y, @weak u => move |x| { /// println!("v as y: {}, u: {}, x: {}", y, u, x); /// }); /// /// closure(3); /// ``` /// /// ### Providing a default return value if upgrading a weak reference fails /// /// You can do it in two different ways: /// /// Either by providing the value yourself using `@default-return`: /// /// ``` /// use glib; /// use glib_macros::clone; /// use std::rc::Rc; /// /// let v = Rc::new(1); /// let closure = clone!(@weak v => @default-return 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 `@default-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 => @default-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 self as this => 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 self.v as v => move |x| { /// println!("self.v: {}", v); /// }); /// # closure(2); /// } /// } /// ``` #[proc_macro] #[proc_macro_error] pub fn clone(item: TokenStream) -> TokenStream { 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 a the `@watch` directive. 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-allow-none` and `@strong` captures are also supported and behave the same as in /// [`clone!`](crate::clone!), as is aliasing captures with the `as` keyword. 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 other as b => 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] #[proc_macro_error] pub fn closure(item: TokenStream) -> TokenStream { 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] #[proc_macro_error] pub fn closure_local(item: TokenStream) -> TokenStream { closure::closure_inner(item, "new_local") } /// Derive macro for register a rust enum in the glib type system and derive the /// 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, /// } /// ``` /// /// [`glib::Value`]: ../glib/value/struct.Value.html #[proc_macro_derive(Enum, attributes(enum_type, enum_value))] #[proc_macro_error] pub fn enum_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let gen = enum_derive::impl_enum(&input); gen.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 needs 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, /// } /// ``` /// /// [`glib::Value`]: ../glib/value/struct.Value.html #[proc_macro_attribute] #[proc_macro_error] pub fn flags(attr: TokenStream, item: TokenStream) -> TokenStream { let mut name = NestedMetaItem::::new("name") .required() .value_required(); if let Err(e) = parse_nested_meta_items_from_stream(attr.into(), &mut [&mut name]) { return e.to_compile_error().into(); } let attr_meta = AttrInput { enum_name: name.value.unwrap(), }; let input = parse_macro_input!(item as DeriveInput); let gen = flags_attribute::impl_flags(attr_meta, &input); gen.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))] #[proc_macro_error] pub fn error_domain_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let gen = error_domain_derive::impl_error_domain(&input); gen.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 implemention 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))] #[proc_macro_error] pub fn boxed_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let gen = boxed_derive::impl_boxed(&input); gen.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 implemention 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))] #[proc_macro_error] pub fn shared_boxed_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let gen = shared_boxed_derive::impl_shared_boxed(&input); gen.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 provide, 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() /// } /// ``` /// /// [`ObjectSubclass`]: ../glib/subclass/types/trait.ObjectSubclass.html #[proc_macro_attribute] #[proc_macro_error] pub fn object_subclass(_attr: TokenStream, item: TokenStream) -> TokenStream { use proc_macro_error::abort_call_site; match syn::parse::(item) { Ok(input) => object_subclass_attribute::impl_object_subclass(&input).into(), Err(_) => abort_call_site!(object_subclass_attribute::WRONG_PLACE_MSG), } } /// 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 present, the macro /// will use the provided value instead of the default. /// /// `Prerequisites` is interfaces for types that require a specific base class or interfaces. /// /// ```ignore /// type Prerequisites = (); /// ``` /// /// [`ObjectInterface`]: ../glib/subclass/interface/trait.ObjectInterface.html #[proc_macro_attribute] #[proc_macro_error] pub fn object_interface(_attr: TokenStream, item: TokenStream) -> TokenStream { use proc_macro_error::abort_call_site; match syn::parse::(item) { Ok(input) => object_interface_attribute::impl_object_interface(&input).into(), Err(_) => abort_call_site!(object_interface_attribute::WRONG_PLACE_MSG), } } /// 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))] #[proc_macro_error] pub fn variant_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); variant_derive::impl_variant(input) } #[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_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: glib::value::ToValue + glib::HasParamSpec` /// If you have declared a newtype as /// ```rust /// struct MyInt(i32); /// ``` /// you can use it as a property by deriving `glib::ValueDelegate`. /// /// ### Types with inner mutability /// The trait `glib::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 `glib::HasParamSpec` /// If you have encountered a type `T: glib::value::ToValue`, inside the `gtk-rs` crate, which doesn't implement `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 `glib::HasParamSpec` on that type. /// /// # Example /// ``` /// use std::cell::RefCell; /// use std::marker::PhantomData; /// use std::sync::Mutex; /// 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()); /// } /// ``` #[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] #[proc_macro_error] pub fn derived_properties(_attr: TokenStream, item: TokenStream) -> TokenStream { use proc_macro_error::abort_call_site; match syn::parse::(item) { Ok(input) => derived_properties_attribute::impl_derived_properties(&input).into(), Err(_) => abort_call_site!(derived_properties_attribute::WRONG_PLACE_MSG), } } /// # 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() } rust-glib-macros-0.18-0.18.2/src/object_interface_attribute.rs000066400000000000000000000036161467454077200241140ustar00rootroot00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; use quote::quote; pub const WRONG_PLACE_MSG: &str = "This macro should be used on `impl` block for `glib::ObjectInterface` trait"; pub fn impl_object_interface(input: &syn::ItemImpl) -> TokenStream { let mut has_prerequisites = false; for item in &input.items { if let syn::ImplItem::Type(type_) = item { let name = type_.ident.to_string(); if name == "Prerequisites" { has_prerequisites = true; } } } let syn::ItemImpl { attrs, generics, trait_, self_ty, unsafety, items, .. } = &input; let prerequisites_opt = if has_prerequisites { None } else { Some(quote!( type Prerequisites = (); )) }; let crate_ident = crate::utils::crate_ident_new(); let trait_path = match &trait_ { Some(path) => &path.1, None => abort_call_site!(WRONG_PLACE_MSG), }; quote! { #(#attrs)* #unsafety impl #generics #trait_path for #self_ty { #prerequisites_opt #(#items)* } unsafe impl #crate_ident::subclass::interface::ObjectInterfaceType for #self_ty { #[inline] fn type_() -> #crate_ident::Type { static ONCE: ::std::sync::Once = ::std::sync::Once::new(); static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID; ONCE.call_once(|| { let type_ = #crate_ident::subclass::register_interface::(); unsafe { TYPE = type_; } }); unsafe { TYPE } } } } } rust-glib-macros-0.18-0.18.2/src/object_subclass_attribute.rs000066400000000000000000000117101467454077200237650ustar00rootroot00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; use quote::quote; pub const WRONG_PLACE_MSG: &str = "This macro should be used on `impl` block for `glib::ObjectSubclass` trait"; pub fn impl_object_subclass(input: &syn::ItemImpl) -> TokenStream { 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 &input.items { 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 syn::ItemImpl { attrs, generics, trait_, self_ty, items, .. } = &input; let crate_ident = crate::utils::crate_ident_new(); 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;)); let trait_path = match &trait_ { Some(path) => &path.1, None => abort_call_site!(WRONG_PLACE_MSG), }; quote! { #(#attrs)* 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::INIT_TYPE_DATA; unsafe { ::std::ptr::NonNull::from(&mut DATA) } } #[inline] fn type_() -> #crate_ident::Type { static ONCE: ::std::sync::Once = ::std::sync::Once::new(); ONCE.call_once(|| { #crate_ident::subclass::register_type::(); }); unsafe { let data = Self::type_data(); let type_ = data.as_ref().type_(); type_ } } } #[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 } } } } rust-glib-macros-0.18-0.18.2/src/properties.rs000066400000000000000000000626521467454077200207440ustar00rootroot00000000000000// 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 proprety description Ok(Self { attrs_span, field_ident, ty, name, override_class, override_interface, nullable, get, set, member, builder, builder_fields, is_construct_only, }) } } 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>::Value as #crate_ident::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::ParamSpecBuilderExt; use #crate_ident::once_cell::sync::Lazy; static PROPERTIES: Lazy<[#crate_ident::ParamSpec; #n_props]> = Lazy::new(|| [ #(#param_specs,)* ]); PROPERTIES.as_ref() } ) } 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>::Value = (#expr)(&self); ::std::convert::From::from(value) } ), (None, MaybeCustomFn::Default) => quote!( DerivedPropertiesEnum::#enum_ident => #crate_ident::PropertyGet::get(&self.#field_ident, |v| ::std::convert::From::from(v)) ), (Some(member), MaybeCustomFn::Default) => quote!( DerivedPropertiesEnum::#enum_ident => #crate_ident::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>::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::PropertySet::set( &self.#field_ident, #crate_ident::Value::get(value)#expect ); } ), (Some(member), MaybeCustomFn::Default) => quote!( DerivedPropertiesEnum::#enum_ident => { #crate_ident::PropertySetNested::set_nested( &self.#field_ident, move |v| v.#member = #crate_ident::Value::get(value)#expect ); } ), }; quote_spanned!(span=> #body) }) }); quote!( 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().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=> pub fn #ident(&self) -> <#ty as #crate_ident::Property>::Value { self.property::<<#ty as #crate_ident::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>::Value as #crate_ident::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=> 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().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=> 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(props: &[PropDesc]) -> Vec { let crate_ident = crate_ident_new(); let emit_fns = props.iter().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=> pub fn #fn_ident(&self) { self.notify_by_pspec( &<::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 { 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(&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 signatures = getset_properties .iter() .chain(connect_prop_notify.iter()) .chain(notify_prop.iter()) .map(|item| &item.sig); let trait_def = quote! { pub trait #trait_ident { #(#signatures;)* } }; let impls = getset_properties .into_iter() .chain(connect_prop_notify) .chain(notify_prop) .map(|mut item| { item.vis = syn::Visibility::Inherited; item }); quote! { #trait_def impl #trait_ident for #wrapper_type { #(#impls)* } } } else { quote! { #[allow(dead_code)] impl #wrapper_type { #(#getset_properties)* #(#connect_prop_notify)* #(#notify_prop)* } } }; let expanded = quote! { use #crate_ident::{PropertyGet, PropertySet, ToValue}; #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) } rust-glib-macros-0.18-0.18.2/src/shared_boxed_derive.rs000066400000000000000000000277541467454077200225410ustar00rootroot00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::{Ident, TokenStream}; use proc_macro_error::abort_call_site; 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) -> proc_macro2::TokenStream { let name = &input.ident; let refcounted_type = match refcounted_type(input) { Some(p) => p, _ => { abort_call_site!("#[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 found = parse_nested_meta_items( &input.attrs, "shared_boxed_type", &mut [&mut gtype_name, &mut nullable], ); match found { Ok(None) => { abort_call_site!( "#[derive(glib::SharedBoxed)] requires #[shared_boxed_type(name = \"SharedBoxedTypeName\")]" ) } Err(e) => return e.to_compile_error(), _ => (), }; let gtype_name = gtype_name.value.unwrap(); let nullable = nullable.found || nullable.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! {} }; quote! { impl #crate_ident::subclass::shared::SharedType for #name { const NAME: &'static ::core::primitive::str = #gtype_name; 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::StaticType for #name { #[inline] fn static_type() -> #crate_ident::Type { static ONCE: ::std::sync::Once = ::std::sync::Once::new(); static mut TYPE_: #crate_ident::Type = #crate_ident::Type::INVALID; ONCE.call_once(|| { let type_ = #crate_ident::subclass::shared::register_shared_type::<#name>(); unsafe { TYPE_ = type_; } }); unsafe { TYPE_ } } } 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::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::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::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::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) } } } } rust-glib-macros-0.18-0.18.2/src/utils.rs000066400000000000000000000201571467454077200177020ustar00rootroot00000000000000// 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 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()); } } rust-glib-macros-0.18-0.18.2/src/value_delegate_derive.rs000066400000000000000000000145111467454077200230430ustar00rootroot00000000000000// 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::types::StaticType for #ident { fn static_type() -> glib::types::Type { <#delegated_ty as #crate_ident::types::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::HasParamSpec>::param_spec_builder() } } }; Ok(res.into()) } rust-glib-macros-0.18-0.18.2/src/variant_derive.rs000066400000000000000000000650551467454077200215520ustar00rootroot00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use heck::ToKebabCase; use proc_macro::TokenStream; use proc_macro_error::abort; 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) -> TokenStream { match input.data { Data::Struct(data_struct) => { 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 { derive_variant_for_c_enum(input.ident, input.generics, data_enum, mode) } } Data::Union(..) => { panic!("#[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::StaticVariantType for #ident #type_generics #where_clause { #[inline] fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> { static TYP: #glib::once_cell::sync::Lazy<#glib::VariantType> = #glib::once_cell::sync::Lazy::new(|| { let mut builder = #glib::GStringBuilder::new("("); #( { let typ = <#types as #glib::StaticVariantType>::static_variant_type(); builder.append(typ.as_str()); } )* builder.append_c(')'); #glib::VariantType::from_string(builder.into_string()).unwrap() }); ::std::borrow::Cow::Borrowed(&*TYP) } } }; let to_variant = quote! { impl #impl_generics #glib::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::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::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::StaticVariantType for #ident #type_generics #where_clause { #[inline] fn static_variant_type() -> ::std::borrow::Cow<'static, #glib::VariantTy> { static TYP: #glib::once_cell::sync::Lazy<#glib::VariantType> = #glib::once_cell::sync::Lazy::new(|| unsafe { let ptr = #glib::ffi::g_string_sized_new(16); #glib::ffi::g_string_append_c(ptr, b'(' as _); #( { let typ = <#types as #glib::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 ) }); ::std::borrow::Cow::Borrowed(&*TYP) } } }; let to_variant = quote! { impl #impl_generics #glib::ToVariant for #ident #type_generics #where_clause { fn to_variant(&self) -> #glib::Variant { #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([ #( #glib::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::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::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::ToVariant for #ident #type_generics #where_clause { #[inline] fn to_variant(&self) -> #glib::Variant { #glib::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::ToVariant::to_variant(&()) } } }; let from_variant = quote! { impl #impl_generics #glib::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) } }; let derived = quote! { #static_variant_type #to_variant #from_variant }; derived.into() } 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, ) -> TokenStream { 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 { .. } => abort!(v, "#[variant_enum(enum) only allowed with C-style enums using #[derive(glib::Enum)]"), EnumMode::Flags { .. } => abort!(v, "#[variant_enum(flags) only allowed with bitflags using #[glib::flags]"), _ => (), } } 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::ToVariant::to_variant(&( #tag, #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([ #(#glib::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::ToVariant::to_variant(&( #tag, #glib::Variant::tuple_from_iter(::std::iter::IntoIterator::into_iter([ #(#glib::ToVariant::to_variant(&#field_names2)),* ])) )) } }, syn::Fields::Unit => { quote! { Self::#ident => #glib::ToVariant::to_variant(&( #tag, #glib::ToVariant::to_variant(&()) )) } }, } }); 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::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::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::ToVariant::to_variant(&( #tag, #glib::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::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::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!(), }; let derived = quote! { impl #impl_generics #glib::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::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::FromVariant for #ident #type_generics #where_clause { fn from_variant(variant: &#glib::Variant) -> ::core::option::Option { let (tag, value) = <(#repr, #glib::Variant) as #glib::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 } } } }; derived.into() } 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::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::ToVariant::to_variant(&(*self as #repr)) }, quote! { let value = <#repr as #glib::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::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::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::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::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) }) }, ), }; let derived = quote! { impl #impl_generics #glib::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::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::ToVariant>::to_variant(&v) } } impl #impl_generics #glib::FromVariant for #ident #type_generics #where_clause { fn from_variant(variant: &#glib::Variant) -> ::core::option::Option { #from_variant } } }; derived.into() } fn get_enum_mode(attrs: &[syn::Attribute]) -> EnumMode { let attr = attrs.iter().find(|a| a.path().is_ident("variant_enum")); let attr = match attr { Some(attr) => attr, None => return 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); } Some("enum") => { mode = EnumMode::Enum { repr: false }; } Some("flags") => { mode = EnumMode::Flags { repr: false }; } _ => abort!(meta.path, "unknown type in #[variant_enum] attribute"), } Ok(()) }) .unwrap(); match mode { EnumMode::String if repr_attr.is_some() => { let repr_attr = repr_attr.unwrap(); let repr = get_repr(attrs).unwrap_or_else(|| { abort!( 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, } } rust-glib-macros-0.18-0.18.2/tests/000077500000000000000000000000001467454077200165425ustar00rootroot00000000000000rust-glib-macros-0.18-0.18.2/tests/clone.rs000066400000000000000000000271661467454077200202240ustar00rootroot00000000000000// 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 v = Rc::new(1); let _ = clone!(@strong v => @default-return None::, move || {println!("foo"); 1}); let v = Rc::new(1); let _ = clone!(@weak v => @default-return None::, move || {println!("foo"); Some(1)}); let v = "123"; let _ = clone!(@to-owned v => @default-return None::, move || {println!("foo"); 1}); } const TESTS: &[(&str, &str)] = &[ ( "clone!( => move || {})", "If you have nothing to clone, no need to use this macro!", ), ( "clone!(|| {})", "If you have nothing to clone, no need to use this macro!", ), ( "clone!(|a, b| {})", "If you have nothing to clone, no need to use this macro!", ), ( "clone!(@weak a, @weak b => |x| {})", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_3.rs:1:86 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak a, @weak b => |x| {}); } | ^"#, ), ( "clone!(@weak a, @weak b => || {})", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_4.rs:1:86 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak a, @weak b => || {}); } | ^"#, ), ( "clone!(@weak a, @weak b => |x| println!(\"a\"))", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_5.rs:1:86 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak a, @weak b => |x| println!("a")); } | ^"#, ), ( "clone!(@weak a, @weak b => || println!(\"a\"))", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_6.rs:1:86 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak a, @weak b => || println!("a")); } | ^"#, ), ( "clone!(@weak a => |x| {})", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_7.rs:1:77 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak a => |x| {}); } | ^"#, ), ( "clone!(@weak a => || {})", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_8.rs:1:77 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak a => || {}); } | ^"#, ), ( "clone!(@weak a => |x| println!(\"a\"))", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_9.rs:1:77 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak a => |x| println!("a")); } | ^"#, ), ( "clone!(@weak a => || println!(\"a\"))", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_10.rs:1:77 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak a => || println!("a")); } | ^"#, ), ( "clone!(@strong self => move |x| {})", r#"error: Can't use `self` as variable name. Try storing it in a temporary variable or rename it using `as`. --> test_11.rs:1:74 | 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: `self.v`: Field accesses are not allowed as is, you must rename it! --> test_12.rs:1:79 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@strong self.v => move |x| {}); } | ^ "#, ), ( "clone!(@weak v => @default-return false, || {})", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_13.rs:1:100 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak v => @default-return false, || {}); } | ^"#, ), ( "clone!(@weak v => @default-return false, || println!(\"a\"))", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_14.rs:1:100 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak v => @default-return false, || println!("a")); } | ^"#, ), ( "clone!(@weak v => @default-return false, |bla| {})", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_15.rs:1:100 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak v => @default-return false, |bla| {}); } | ^ "#, ), ( "clone!(@weak v => @default-return false, |bla| println!(\"a\"))", r#"error: Closure needs to be "moved" so please add `move` before closure --> test_16.rs:1:100 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak v => @default-return false, |bla| println!("a")); } | ^ "#, ), ( "clone!(@weak v => default-return false, move || {})", r#"error: Missing `@` before `default-return` --> test_17.rs:1:77 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak v => default-return false, move || {}); } | ^^^^^^^ "#, ), ( "clone!(@weak v => @default-return false move || {})", r#"error: Expected `,` after `@default-return false`, found `move` --> test_18.rs:1:99 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@weak v => @default-return false move || {}); } | ^^^^ "#, ), ( "clone!(@yolo v => move || {})", r#"error: Unknown keyword `yolo`, only `weak`, `weak-allow-none`, `to-owned` and `strong` are allowed --> test_19.rs:1:67 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@yolo v => move || {}); } | ^^^^ "#, ), ( "clone!(v => move || {})", r#"error: Unexpected ident `v`: you need to specify if this is a weak or a strong clone. --> test_20.rs:1:66 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(v => move || {}); } | ^ "#, ), ( "clone!(@strong v => {println!(\"foo\");})", r#"error: Missing `move` and closure declaration --> test_21.rs:1:79 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@strong v => {println!("foo");}); } | ^^^^^^^^^^^^^^^^^^ "#, ), ( "clone!(@strong v, @default-return lol => move || {println!(\"foo\");})", r#"error: `@default-return` should be after `=>` --> test_22.rs:1:78 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@strong v, @default-return lol => move || {println!("foo");}); } | ^^^^^^^ "#, ), ( "clone!(@default-return lol, @strong v => move || {println!(\"foo\");})", r#"error: `@default-return` should be after `=>` --> test_23.rs:1:67 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@default-return lol, @strong v => move || {println!("foo");}); } | ^^^^^^^ "#, ), // The async part! ( "clone!(@strong v => async || {println!(\"foo\");})", r#"error: Expected `move` after `async`, found `|` --> test_24.rs:1:85 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@strong v => async || {println!("foo");}); } | ^"#, ), ( "clone!(@strong v => async {println!(\"foo\");})", r#"error: Expected `move` after `async`, found `{` --> test_25.rs:1:85 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@strong v => async {println!("foo");}); } | ^^^^^^^^^^^^^^^^^^ "#, ), ( "clone!(@strong v => move || async {println!(\"foo\");})", r#"error: Expected `move` after `async`, found `{` --> test_26.rs:1:93 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@strong v => move || async {println!("foo");}); } | ^^^^^^^^^^^^^^^^^^ "#, ), ( "clone!(@strong v => move || async println!(\"foo\");)", r#"error: Expected `move` after `async`, found `println` --> test_27.rs:1:93 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@strong v => move || async println!("foo");); } | ^^^^^^^ "#, ), ( "clone!(@strong v => move || async move println!(\"foo\");)", r#"error: Expected block after `| async move` --> test_28.rs:1:98 | 1 | fn main() { use glib::clone; let v = std::rc::Rc::new(1); clone!(@strong v => move || async move 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] = &[ "clone!(@weak v => @default-return (), move || println!(\"{}\", v);)", "clone!(@weak v => @default-return (()), move || println!(\"{}\", v);)", "clone!(@weak v => @default-return ( () ), move || println!(\"{}\", v);)", "clone!(@weak v => @default-return ( ), move || println!(\"{}\", v);)", ]; // Ensures that no warning are emitted if the default-return 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); } } rust-glib-macros-0.18-0.18.2/tests/properties.rs000066400000000000000000000420411467454077200213050ustar00rootroot00000000000000// 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)] 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 once_cell::sync::OnceCell; 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::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 = |_| 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"); } } } 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()) ); } mod ext_trait { use glib::subclass::object::DerivedObjectProperties; use glib::ObjectExt; use glib::subclass::{prelude::ObjectImpl, types::ObjectSubclass}; use glib_macros::Properties; use std::cell::RefCell; pub mod imp { use super::*; #[derive(Properties, Default)] #[properties(wrapper_type = super::Author, ext_trait)] pub struct Author { #[property(get, set)] firstname: RefCell, #[property(get, set)] lastname: RefCell, } #[glib::derived_properties] impl ObjectImpl for Author {} #[glib::object_subclass] impl ObjectSubclass for Author { const NAME: &'static str = "Author"; type Type = super::Author; } } glib::wrapper! { pub struct Author(ObjectSubclass); } impl Author { pub fn new() -> Self { glib::Object::builder().build() } } impl Default for Author { fn default() -> Self { Self::new() } } } #[test] fn ext_trait() { use ext_trait::imp::AuthorPropertiesExt; let author = ext_trait::Author::new(); AuthorPropertiesExt::set_firstname(&author, "John"); AuthorPropertiesExt::set_lastname(&author, "Doe"); assert_eq!(AuthorPropertiesExt::firstname(&author), "John"); assert_eq!(AuthorPropertiesExt::lastname(&author), "Doe"); } #[test] fn keyword_propnames() { mod kw_names { mod imp { use glib::subclass::object::DerivedObjectProperties; use glib::ObjectExt; use std::cell::Cell; use glib::subclass::{prelude::ObjectImpl, types::ObjectSubclass}; 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); } rust-glib-macros-0.18-0.18.2/tests/test.rs000066400000000000000000000510411467454077200200700ustar00rootroot00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use glib::{ 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 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 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 {} } pub trait FooExt: IsA + 'static { fn test(&self) { let _self = self.as_ref().downcast_ref::().unwrap().imp(); unimplemented!() } } impl> FooExt for O {} glib::wrapper! { pub struct Foo(ObjectSubclass); } } } #[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 self as obj => 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 self.obj as 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 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 obj1 as a, @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 a_struct.a as 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::<()>(&[]); } } rust-glib-macros-0.18-0.18.2/tests/value_delegate_derive.rs000066400000000000000000000205261467454077200234210ustar00rootroot00000000000000use glib::{value::FromValue, StaticType, ToValue, 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 } ); }