glib-macros-0.14.1/.cargo_vcs_info.json0000644000000001120000000000000133070ustar { "git": { "sha1": "f808d473bf19fa42e8fbd9f8cf1f5bd25c091c1d" } } glib-macros-0.14.1/COPYRIGHT000064400000000000000000000012130000000000000133530ustar 00000000000000The gtk-rs Project is licensed under the MIT license, see the LICENSE file or . Copyrights in the gtk-rs Project project are retained by their contributors. No copyright assignment is required to contribute to the gtk-rs Project project. For full authorship information, see the version control history. This project provides interoperability with various GNOME libraries but doesn't distribute any parts of them. Distributing compiled libraries and executables that link to those libraries may be subject to terms of the GNU LGPL or other licenses. For more information check the license of each GNOME library. glib-macros-0.14.1/Cargo.toml0000644000000024710000000000000113170ustar # 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 believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "glib-macros" version = "0.14.1" 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" [lib] proc-macro = true [dependencies.anyhow] version = "1" [dependencies.heck] version = "0.3" [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 = "1.0" features = ["full"] default-features = false [dev-dependencies.glib] version = "0.14.0" glib-macros-0.14.1/Cargo.toml.orig000064400000000000000000000012760000000000000147600ustar 00000000000000[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.14.1" keywords = ["glib", "gtk-rs", "gnome", "GUI"] repository = "https://github.com/gtk-rs/gtk-rs-core" license = "MIT" edition = "2018" [dependencies] anyhow = "1" heck = "0.3" proc-macro-error = "1.0" proc-macro2 = "1.0" quote = "1.0" syn = { version = "1.0", features = ["full"], default-features = false } proc-macro-crate = "1.0" [lib] proc-macro = true [dev-dependencies] glib = { path = "../glib", version = "0.14.0" } glib-macros-0.14.1/LICENSE000064400000000000000000000020000000000000000130600ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. glib-macros-0.14.1/src/clone.rs000064400000000000000000000575060000000000000143350ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use crate::utils::crate_ident_new; use proc_macro::token_stream::IntoIter as ProcIter; use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; use std::iter::Peekable; #[derive(Clone, Copy, Debug)] enum BorrowKind { Weak, WeakAllowNone, Strong, } impl BorrowKind { fn to_str(self) -> &'static str { match self { Self::Weak => "@weak", Self::WeakAllowNone => "@weak-allow-none", Self::Strong => "@strong", } } } 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, ), } } 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))) => { 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(), r, ) } (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 Peekable, ) -> Result<(), TokenCheck> { let mut tokens = String::new(); for token in tokens_to_check { if let Some(next) = parts.next() { if *token != next { return Err(TokenCheck::UnexpectedToken( tokens, token.to_str().to_owned(), )); } tokens.push_str(token.to_str()); } else { return Err(TokenCheck::UnexpectedEnd(tokens)); } } Ok(()) } #[doc(alias = "get_full_ident")] fn full_ident(parts: &mut Peekable, borrow_kind: BorrowKind) -> String { 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 { panic!("Unexpected `.` after `{}`", borrow_kind.to_str()); } prev_is_ident = false; name.push('.'); parts.next(); } else if name.is_empty() { panic!("Expected ident, found `{}`", p_s); } else { panic!("Expected ident, found `{}` after `{}`", p_s, 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() => panic!("Expected ident, found `{}`", x.to_string()), Some(x) => panic!("Expected ident, found `{}` after `{}`", x.to_string(), name), None => panic!("Unexpected end after ident `{}`", name), } } if name.is_empty() { panic!( "Expected ident, found `{}`", parts.next().unwrap().to_string() ); } name } #[doc(alias = "get_keyword")] fn keyword(parts: &mut Peekable) -> String { let mut ret = String::new(); let mut prev_is_ident = false; let mut stored = false; 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(); } ret } fn parse_ident(parts: &mut Peekable, elements: &mut Vec) { let borrow_kind = match keyword(parts).as_str() { "strong" => BorrowKind::Strong, "weak" => BorrowKind::Weak, "weak-allow-none" => BorrowKind::WeakAllowNone, "default-return" => panic!("`@default-return` should be after `=>`"), "default-panic" => panic!("`@default-panic` should be after `=>`"), k => panic!( "Unknown keyword `{}`, only `weak`, `weak-allow-none` and `strong` are allowed", k, ), }; let name = full_ident(parts, borrow_kind); let alias = match parts.peek() { Some(TokenTree::Ident(p)) if p.to_string() == "as" => { parts.next(); match parts.next() { Some(TokenTree::Ident(i)) => Some(i.to_string()), Some(x) => panic!( "Expected ident after `as` keyword, found `{}`", x.to_string() ), None => panic!("Unexpected end after `as` keyword"), } } Some(TokenTree::Ident(p)) => panic!("Unexpected `{}`", p.to_string()), _ => None, }; if name == "self" && alias.is_none() { panic!( "Can't use `self` as variable name. Try storing it in a temporary variable or \ rename it using `as`." ); } else if name.ends_with('.') { panic!("Invalid variable name: `{}`", name); } else if name.contains('.') && alias.is_none() { panic!( "`{}`: Field accesses are not allowed as is, you must rename it!", name ); } elements.push(ElemToClone { name, alias, borrow_kind, }); } 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(g.stream().into_iter().peekable()), delimiter_to_string(g.delimiter(), false), ) } #[doc(alias = "get_expr")] fn expr(parts: &mut Peekable) -> String { let mut ret = String::new(); let mut total = 0; 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 => panic!("Unexpected token `{}` after `@default-return`", x), }, Some(TokenTree::Group(g)) => return group_to_string(&g), None => panic!("Unexpected end after `@default-return`"), }; 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 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 ret; } ret.push_str(&x.to_string()) } None => panic!( "Unexpected end after `{}`. Did you forget a `,` after the @default-return value?", ret ), } parts.next(); } } #[doc(alias = "get_return_kind")] fn return_kind(parts: &mut Peekable) -> WrapperKind { match check_tokens( &[SimpleToken::Ident("default"), SimpleToken::Punct("-")], parts, ) { Err(TokenCheck::UnexpectedToken(tokens, unexpected_token)) => { panic!("Unknown keyword `{}{}`", tokens, unexpected_token); } Err(TokenCheck::UnexpectedEnd(tokens)) => { panic!("Unexpected end after tokens `{}`", tokens); } Ok(()) => {} } match parts.next() { Some(TokenTree::Ident(i)) => { let i_s = i.to_string(); if i_s == "panic" { return WrapperKind::DefaultPanic; } else if i_s != "return" { panic!("Unknown keyword `@default-{}`", i_s); } } Some(x) => panic!("Unknown token `{}` after `@default-`", x.to_string()), None => panic!("Unexpected end after `@default-`"), } WrapperKind::DefaultReturn(expr(parts)) } fn parse_return_kind(parts: &mut Peekable) -> Option { match parts.peek() { Some(TokenTree::Punct(p)) if p.to_string() == "@" => {} None => panic!("Unexpected end 2"), _ => return None, } parts.next(); let ret = return_kind(parts); match check_tokens(&[SimpleToken::Punct(",")], parts) { Err(TokenCheck::UnexpectedToken(_, unexpected_token)) => { panic!( "Expected `,` after `{}`, found `{}`", ret.to_str(), unexpected_token ); } Err(TokenCheck::UnexpectedEnd(tokens)) => { panic!("Expected `,` after `{}{}`", ret.to_str(), tokens); } 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 Peekable) { match parts.next() { Some(TokenTree::Ident(i)) if i.to_string() == "move" => {} // The next checks are just for better error messages. Some(TokenTree::Ident(i)) => { panic!("Expected `move` after `async`, found `{}`", i.to_string()); } Some(TokenTree::Punct(p)) => { panic!("Expected `move` after `async`, found `{}`", p.to_string()); } Some(TokenTree::Group(g)) => { panic!( "Expected `move` after `async`, found `{}`", delimiter_to_string(g.delimiter(), true), ); } _ => panic!("Expected `move` after `async`"), } } fn check_async_syntax(parts: &mut Peekable) -> BlockKind { check_move_after_async(parts); match parts.peek() { Some(TokenTree::Punct(p)) if p.to_string() == "|" => { parts.next(); BlockKind::AsyncClosure(closure(parts)) } Some(TokenTree::Punct(p)) => { panic!( "Expected closure or block after `async move`, found `{}`", p.to_string() ); } Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => BlockKind::AsyncBlock, Some(TokenTree::Group(g)) => { panic!( "Expected closure or block after `async move`, found `{}`", delimiter_to_string(g.delimiter(), true), ); } _ => panic!("Expected closure or block after `async move`"), } } // Returns `true` if this is an async context. fn check_before_closure(parts: &mut Peekable) -> BlockKind { 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 ret = return_kind(parts); panic!("Missing `@` before `{}`", ret.keyword()); } Some(TokenTree::Punct(p)) if p.to_string() == "|" => { panic!("Closure needs to be \"moved\" so please add `move` before closure") } _ => panic!("Missing `move` and closure declaration"), }; parts.next(); if is_async { return check_async_syntax(parts); } match parts.next() { Some(TokenTree::Punct(p)) if p.to_string() == "|" => {} Some(x) => panic!("Expected closure, found `{}`", x.to_string()), None => panic!("Expected closure"), } 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 => { BlockKind::ClosureWrappingAsync(closure) } // The next matchings are for better error messages. Some(TokenTree::Punct(p)) => { panic!( "Expected block after `| async move`, found `{}`", p.to_string() ); } Some(TokenTree::Group(g)) => { panic!( "Expected block after `| async move`, found `{}`", delimiter_to_string(g.delimiter(), true), ); } _ => panic!("Expected block after `| async move`"), } } _ => BlockKind::Closure(closure), } } #[doc(alias = "get_closure")] fn closure(parts: &mut Peekable) -> Vec { let mut ret = Vec::new(); loop { match parts.next() { Some(TokenTree::Punct(p)) if p.to_string() == "|" => break, Some(x) => ret.push(x), None => panic!("Unexpected end 3"), } } 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: Peekable, 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 = item.into_iter().peekable(); let mut elements = Vec::new(); let mut prev_is_ident = false; loop { 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 == "@" { parse_ident(&mut parts, &mut elements); prev_is_ident = true; } else if p_s == "," { if !prev_is_ident { panic!("Unexpected `,`"); } prev_is_ident = false; } else if p_s == "|" { if elements.is_empty() { panic!("If you have nothing to clone, no need to use this macro!"); } else { panic!("Expected `=>` before closure"); } } } Some(TokenTree::Ident(i)) => { panic!( "Unexpected ident `{}`: you need to specify if this is a weak or a strong \ clone.", i.to_string(), ); } Some(t) => panic!("Unexpected token `{}`", t.to_string()), None => panic!("Unexpected end 4"), } } if elements.is_empty() { panic!("If you have nothing to clone, no need to use this macro!"); } let return_kind = parse_return_kind(&mut parts); let kind = check_before_closure(&mut parts); build_closure(parts, elements, return_kind, kind) } glib-macros-0.14.1/src/downgrade_derive/enums.rs000064400000000000000000000075360000000000000176720ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use super::fields::{derive_downgrade_fields, DowngradeStructParts}; use proc_macro::TokenStream; use quote::{format_ident, quote}; use syn::{Generics, Ident}; /// 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 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) -> Option { Some(match self {#( #upgrade_variants ),*}) } } }; derived.into() } glib-macros-0.14.1/src/downgrade_derive/fields.rs000064400000000000000000000130620000000000000200000ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::{Fields, FieldsNamed, FieldsUnnamed, Ident, Type}; /// 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 { match fields { Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { let fields: Vec = unnamed .into_pairs() .map(|pair| pair.into_value()) .map(|field| field.ty) .collect(); let weak_fields: Vec<_> = fields .iter() .map(|ty| { quote! { <#ty as glib::clone::Downgrade>::Weak } }) .collect(); let field_ident: Vec = (0..fields.len()).map(|i| format_ident!("_{}", i)).collect(); DowngradeStructParts { weak_fields: quote! { (#( #weak_fields ),*) }, end_of_struct: quote!(;), destruct: quote! { (#( ref #field_ident ),*) }, downgrade: quote! { (#( glib::clone::Downgrade::downgrade(#field_ident) ),*) }, upgrade: quote! { (#( glib::clone::Upgrade::upgrade(#field_ident)? ),*) }, } } Fields::Named(FieldsNamed { named, .. }) => { let fields: Vec<(Ident, Type)> = named .into_pairs() .map(|pair| pair.into_value()) .map(|field| (field.ident.expect("Field ident is specified"), field.ty)) .collect(); let weak_fields: Vec<_> = fields .iter() .map(|(ident, ty)| { quote! { #ident: <#ty as glib::clone::Downgrade>::Weak } }) .collect(); let field_ident: Vec<_> = fields.iter().map(|(ident, _ty)| ident).collect(); DowngradeStructParts { weak_fields: quote! { {#( #weak_fields ),*} }, end_of_struct: quote!(), destruct: quote! { {#( ref #field_ident ),*} }, downgrade: quote! { {#( #field_ident: glib::clone::Downgrade::downgrade(#field_ident) ),*} }, upgrade: quote! { {#( #field_ident: glib::clone::Upgrade::upgrade(#field_ident)? ),*} }, } } Fields::Unit => DowngradeStructParts { weak_fields: quote! {}, end_of_struct: quote! { ; }, destruct: quote! {}, downgrade: quote! {}, upgrade: quote! {}, }, } } glib-macros-0.14.1/src/downgrade_derive/mod.rs000064400000000000000000000012120000000000000173030ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. mod enums; mod fields; mod structs; use proc_macro::TokenStream; use syn::{Data, DeriveInput}; pub fn impl_downgrade(input: DeriveInput) -> TokenStream { match input.data { Data::Struct(data_struct) => { structs::derive_downgrade_for_struct(input.ident, input.generics, data_struct) } Data::Enum(data_enum) => { enums::derive_downgrade_for_enum(input.ident, input.generics, data_enum) } Data::Union(..) => { panic!("#[derive(Downgrade)] is not available for unions."); } } } glib-macros-0.14.1/src/downgrade_derive/structs.rs000064400000000000000000000062170000000000000202450ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use super::fields::{derive_downgrade_fields, DowngradeStructParts}; use proc_macro::TokenStream; use quote::{format_ident, quote}; use syn::{Generics, Ident}; /// 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 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) -> Option { let Self #destruct = self; Some(#ident #upgrade) } } }; derived.into() } glib-macros-0.14.1/src/gboxed_derive.rs000064400000000000000000000133440000000000000160330ustar 00000000000000// 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, find_attribute_meta, find_nested_meta, parse_type_name}; fn gen_option_to_ptr() -> TokenStream { quote! { match s { Some(s) => Box::into_raw(Box::new(s.clone())), 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; 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); assert!(!ptr.is_null()); *Box::from_raw(ptr as *mut #name) } } unsafe impl<'a> #crate_ident::value::FromValue<'a> for &'a #name { type Checker = #crate_ident::value::GenericValueTypeOrNoneChecker; 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); 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; 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); assert!(!ptr.is_null()); *Box::from_raw(ptr as *mut #name) } } unsafe impl<'a> #crate_ident::value::FromValue<'a> for &'a #name { type Checker = #crate_ident::value::GenericValueTypeChecker; 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); 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 { fn to_value_optional(s: 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 } } } } pub fn impl_gboxed(input: &syn::DeriveInput) -> TokenStream { let name = &input.ident; let gtype_name = match parse_type_name(input, "gboxed") { Ok(v) => v, Err(e) => abort_call_site!( "{}: derive(GBoxed) requires #[gboxed(type_name = \"BoxedTypeName\")]", e ), }; let crate_ident = crate_ident_new(); let meta = find_attribute_meta(&input.attrs, "gboxed") .unwrap() .unwrap(); let nullable = find_nested_meta(&meta, "nullable").is_some(); 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 str = #gtype_name; } impl #crate_ident::StaticType for #name { 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 { assert!(TYPE_.is_valid()); TYPE_ } } } impl #crate_ident::value::ValueType for #name { type Type = #name; } impl #crate_ident::value::ToValue for #name { fn to_value(&self) -> #crate_ident::Value { unsafe { let ptr: *mut #name = Box::into_raw(Box::new(self.clone())); let mut value = #crate_ident::Value::from_type(<#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 } } fn value_type(&self) -> #crate_ident::Type { <#name as #crate_ident::StaticType>::static_type() } } #impl_to_value_optional #impl_from_value } } glib-macros-0.14.1/src/gboxed_shared_derive.rs000064400000000000000000000142400000000000000173550ustar 00000000000000// 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, find_attribute_meta, find_nested_meta, parse_type_name}; 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 { fn to_value_optional(s: Option<&Self>) -> #crate_ident::Value { let mut value = #crate_ident::Value::for_value_type::(); unsafe { let ptr = match s { Some(s) => #refcounted_type_prefix::into_raw(s.0.clone()), 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 } } } } 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; 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); 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; 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); 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_gshared_boxed(input: &syn::DeriveInput) -> proc_macro2::TokenStream { let refcounted_type = match refcounted_type(input) { Some(p) => p, _ => abort_call_site!("derive(GSharedBoxed) requires struct MyStruct(T: RefCounted)"), }; let name = &input.ident; let gtype_name = match parse_type_name(input, "gshared_boxed") { Ok(v) => v, Err(e) => abort_call_site!( "{}: derive(GSharedBoxed) requires #[gshared_boxed(type_name = \"SharedTypeName\")]", e ), }; let meta = find_attribute_meta(&input.attrs, "gshared_boxed") .unwrap() .unwrap(); let nullable = find_nested_meta(&meta, "nullable").is_some(); 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 str = #gtype_name; type RefCountedType = #refcounted_type; fn from_refcounted(this: Self::RefCountedType) -> Self { Self(this) } fn into_refcounted(self) -> Self::RefCountedType { self.0 } } impl #crate_ident::StaticType for #name { 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 { 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(<#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 } } fn value_type(&self) -> #crate_ident::Type { <#name as #crate_ident::StaticType>::static_type() } } #impl_to_value_optional #impl_from_value } } glib-macros-0.14.1/src/genum_derive.rs000064400000000000000000000137120000000000000156750ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use heck::{CamelCase, KebabCase}; 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_item_attributes, parse_type_name, ItemAttribute, }; // 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_genum_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_camel_case(); let mut value_nick = name.to_string().to_kebab_case(); let attrs = parse_item_attributes("genum", &v.attrs); let attrs = match attrs { Ok(attrs) => attrs, Err(e) => abort_call_site!( "{}: GEnum enum supports only the following optional attributes: #[genum(name = \"The Cat\", nick = \"chat\")]", e ), }; attrs.into_iter().for_each(|attr| match attr { ItemAttribute::Name(n) => value_name = n, ItemAttribute::Nick(n) => value_nick = n, } ); let value_name = format!("{}\0", value_name); let value_nick = format!("{}\0", value_nick); 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_genum(input: &syn::DeriveInput) -> TokenStream { let name = &input.ident; let crate_ident = crate_ident_new(); let enum_variants = match input.data { Data::Enum(ref e) => &e.variants, _ => abort_call_site!("GEnum only supports enums"), }; let gtype_name = match parse_type_name(input, "genum") { Ok(v) => v, Err(e) => abort_call_site!( "{}: derive(GEnum) requires #[genum(type_name = \"EnumTypeName\")]", e ), }; let from_glib = gen_enum_from_glib(name, enum_variants); let (genum_values, nb_genum_values) = gen_genum_values(name, enum_variants); quote! { impl #crate_ident::translate::IntoGlib for #name { type GlibType = i32; fn into_glib(self) -> i32 { self as i32 } } impl #crate_ident::translate::TryFromGlib for #name { type Error = i32; unsafe fn try_from_glib(value: i32) -> Result { let from_glib = || { #from_glib }; from_glib().ok_or(value) } } impl #crate_ident::translate::FromGlib for #name { 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; 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 { 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 } fn value_type(&self) -> #crate_ident::Type { ::static_type() } } impl #crate_ident::StaticType for #name { 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_genum_values] = [ #genum_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()); TYPE = #crate_ident::translate::from_glib(type_); } }); unsafe { assert!(TYPE.is_valid()); TYPE } } } } } glib-macros-0.14.1/src/gerror_domain_derive.rs000064400000000000000000000031510000000000000174050ustar 00000000000000// 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_name}; pub fn impl_gerror_domain(input: &syn::DeriveInput) -> TokenStream { let name = &input.ident; let crate_ident = crate_ident_new(); let enum_variants = match input.data { Data::Enum(ref e) => &e.variants, _ => abort_call_site!("GErrorDomain only supports enums"), }; let domain_name = match parse_name(input, "gerror_domain") { Ok(v) => v, Err(e) => abort_call_site!( "{}: derive(GErrorDomain) requires #[gerror_domain(name = \"DomainName\")]", e ), }; let from_glib = gen_enum_from_glib(name, enum_variants); quote! { impl #crate_ident::error::ErrorDomain for #name { 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 str as *const _)) }); *QUARK } fn code(self) -> i32 { self as i32 } fn from(value: i32) -> Option where Self: Sized { #from_glib } } } } glib-macros-0.14.1/src/gflags_attribute.rs000064400000000000000000000147160000000000000165570ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use heck::{CamelCase, KebabCase}; 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, LitStr, Variant, Visibility, }; use crate::utils::{ crate_ident_new, find_attribute_meta, find_nested_meta, parse_item_attributes, ItemAttribute, }; // Flag is not registered if it has the #[gflags(skip)] meta fn attribute_has_skip(attrs: &[Attribute]) -> bool { let meta = find_attribute_meta(attrs, "gflags").unwrap(); match meta { None => false, Some(meta) => find_nested_meta(&meta, "skip").is_some(), } } // 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_gflags_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().filter(|v| { !attribute_has_skip(&v.attrs) } ).map(|v| { let name = &v.ident; let mut value_name = name.to_string().to_camel_case(); let mut value_nick = name.to_string().to_kebab_case(); let attrs = parse_item_attributes("gflags", &v.attrs); let attrs = match attrs { Ok(attrs) => attrs, Err(e) => abort_call_site!( "{}: gflags enum supports only the following optional attributes: #[gflags(name = \"The Name\", nick = \"the-nick\")] or #[gflags(skip)]", e ), }; attrs.into_iter().for_each(|attr| match attr { ItemAttribute::Name(n) => value_name = n, ItemAttribute::Nick(n) => value_nick = n, } ); let value_name = format!("{}\0", value_name); let value_nick = format!("{}\0", value_nick); 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! { #visibility struct #enum_name: u32 { #(#recurse)* } } } } pub fn impl_gflags(input: &DeriveInput, gtype_name: &LitStr) -> TokenStream { let visibility = &input.vis; let name = &input.ident; let crate_ident = crate_ident_new(); let enum_variants = match input.data { Data::Enum(ref e) => &e.variants, _ => abort_call_site!("gflags only supports enums"), }; let bitflags = gen_bitflags(name, visibility, enum_variants, &crate_ident); let (gflags_values, nb_gflags_values) = gen_gflags_values(name, enum_variants); quote! { #bitflags impl #crate_ident::translate::IntoGlib for #name { type GlibType = u32; fn into_glib(self) -> u32 { self.bits() } } impl #crate_ident::translate::FromGlib for #name { 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; 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 { 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 } fn value_type(&self) -> #crate_ident::Type { ::static_type() } } impl #crate_ident::StaticType for #name { 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_gflags_values] = [ #gflags_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()); TYPE = #crate_ident::translate::from_glib(type_); } }); unsafe { assert!(TYPE.is_valid()); TYPE } } } } } glib-macros-0.14.1/src/lib.rs000064400000000000000000000343640000000000000140000ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. mod clone; mod downgrade_derive; mod gboxed_derive; mod gboxed_shared_derive; mod genum_derive; mod gerror_domain_derive; mod gflags_attribute; mod object_interface_attribute; mod object_subclass_attribute; mod utils; use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; use syn::{parse_macro_input, DeriveInput, LitStr}; /// 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`]: https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib/macro.g_debug.html /// [`CLONE_MACRO_LOG_DOMAIN`]: https://gtk-rs.org/gtk-rs-core/stable/latest/docs/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); /// ``` /// /// ### 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) } #[proc_macro_derive(GEnum, attributes(genum))] #[proc_macro_error] pub fn genum_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let gen = genum_derive::impl_genum(&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::GErrorDomain)] /// #[gerror_domain(name = "ExFoo")] /// enum Foo { /// Blah, /// Baaz, /// } /// ``` /// /// [`ErrorDomain`]: error/trait.ErrorDomain.html #[proc_macro_derive(GErrorDomain, attributes(gerror_domain))] #[proc_macro_error] pub fn gerror_domain_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let gen = gerror_domain_derive::impl_gerror_domain(&input); gen.into() } /// Derive macro for defining a [`BoxedType`]`::type_` function and /// the [`glib::Value`] traits. /// /// # Example /// /// ``` /// use glib::prelude::*; /// use glib::subclass::prelude::*; /// /// #[derive(Clone, Debug, PartialEq, Eq, glib::GBoxed)] /// #[gboxed(type_name = "MyBoxed")] /// struct MyBoxed(String); /// ``` /// /// [`BoxedType`]: subclass/boxed/trait.BoxedType.html /// [`glib::Value`]: value/struct.Value.html #[proc_macro_derive(GBoxed, attributes(gboxed))] #[proc_macro_error] pub fn gboxed_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let gen = gboxed_derive::impl_gboxed(&input); gen.into() } /// Derive macro for defining a [`SharedType`]`::get_type` function and /// the [`glib::Value`] traits. /// /// # Example /// /// ``` /// use glib::prelude::*; /// use glib::subclass::prelude::*; /// /// #[derive(Clone, Debug, PartialEq, Eq)] /// struct MySharedInner { /// foo: String, /// } /// #[derive(Clone, Debug, PartialEq, Eq, glib::GSharedBoxed)] /// #[gshared_boxed(type_name = "MyShared")] /// struct MyShared(std::sync::Arc); /// ``` /// /// [`SharedType`]: subclass/shared/trait.SharedType.html /// [`glib::Value`]: value/struct.Value.html #[proc_macro_derive(GSharedBoxed, attributes(gshared_boxed))] #[proc_macro_error] pub fn gshared_boxed_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let gen = gboxed_shared_derive::impl_gshared_boxed(&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 `#[gflags(skip)]` attribute. /// /// # Example /// /// ``` /// use glib::prelude::*; /// use glib::subclass::prelude::*; /// /// #[glib::gflags("MyFlags")] /// enum MyFlags { /// #[gflags(name = "Flag A", nick = "nick-a")] /// A = 0b00000001, /// #[gflags(name = "Flag B")] /// B = 0b00000010, /// #[gflags(skip)] /// AB = Self::A.bits() | Self::B.bits(), /// C = 0b00000100, /// } /// ``` /// /// [`glib::Value`]: value/struct.Value.html #[proc_macro_attribute] #[proc_macro_error] pub fn gflags(attr: TokenStream, item: TokenStream) -> TokenStream { let input = parse_macro_input!(item as DeriveInput); let gtype_name = parse_macro_input!(attr as LitStr); let gen = gflags_attribute::impl_gflags(&input, >ype_name); 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::simple::InstanceStruct; /// type Class = glib::subclass::simple::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`]: 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`]: interface/types/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`]: clone/trait.Downgrade.html /// [`glib::clone::Upgrade`]: 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) } glib-macros-0.14.1/src/object_interface_attribute.rs000064400000000000000000000036410000000000000205750ustar 00000000000000// 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 { 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 { assert!(TYPE.is_valid()); TYPE } } } } } glib-macros-0.14.1/src/object_subclass_attribute.rs000064400000000000000000000067030000000000000204560ustar 00000000000000// 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_interfaces = false; let mut has_instance = false; let mut has_class = false; for item in &input.items { match item { syn::ImplItem::Method(method) => { let name = method.sig.ident.to_string(); if name == "new" || name == "with_class" { has_new = true; } } syn::ImplItem::Type(type_) => { let name = type_.ident.to_string(); 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 interfaces_opt = if has_interfaces { None } else { Some(quote!( type Interfaces = (); )) }; let new_opt = if has_new { None } else { Some(quote! { fn new() -> Self { std::default::Default::default() } }) }; let crate_ident = crate::utils::crate_ident_new(); let class_opt = if has_class { None } else { Some(quote!(type Class = #crate_ident::subclass::basic::ClassStruct;)) }; let instance_opt = if has_instance { None } else { Some(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 { #interfaces_opt #class_opt #instance_opt #new_opt #(#items)* } unsafe impl #crate_ident::subclass::types::ObjectSubclassType for #self_ty { fn type_data() -> std::ptr::NonNull<#crate_ident::subclass::TypeData> { static mut DATA: #crate_ident::subclass::TypeData = #crate_ident::subclass::TypeData { type_: #crate_ident::Type::INVALID, parent_class: std::ptr::null_mut(), parent_ifaces: None, class_data: None, private_offset: 0, private_imp_offset: 0, }; unsafe { std::ptr::NonNull::new_unchecked(&mut DATA) } } 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_(); assert!(type_.is_valid()); type_ } } } } } glib-macros-0.14.1/src/utils.rs000064400000000000000000000131730000000000000143650ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use anyhow::{bail, Result}; use proc_macro2::{Ident, Span, TokenStream}; use proc_macro_crate::crate_name; use quote::{quote, quote_spanned}; use syn::{ punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, DeriveInput, Lit, Meta, MetaList, NestedMeta, Variant, }; // find the #[@attr_name] attribute in @attrs pub fn find_attribute_meta(attrs: &[Attribute], attr_name: &str) -> Result> { let meta = match attrs.iter().find(|a| a.path.is_ident(attr_name)) { Some(a) => a.parse_meta(), _ => return Ok(None), }; match meta? { Meta::List(n) => Ok(Some(n)), _ => bail!("wrong meta type"), } } // parse a single meta like: ident = "value" fn parse_attribute(meta: &NestedMeta) -> Result<(String, String)> { let meta = match &meta { NestedMeta::Meta(m) => m, _ => bail!("wrong meta type"), }; let meta = match meta { Meta::NameValue(n) => n, _ => bail!("wrong meta type"), }; let value = match &meta.lit { Lit::Str(s) => s.value(), _ => bail!("wrong meta type"), }; let ident = match meta.path.get_ident() { None => bail!("missing ident"), Some(ident) => ident, }; Ok((ident.to_string(), value)) } #[derive(Debug)] pub enum EnumAttribute { TypeName(String), } pub fn parse_enum_attribute(meta: &NestedMeta) -> Result { let (ident, v) = parse_attribute(meta)?; match ident.as_ref() { "type_name" => Ok(EnumAttribute::TypeName(v)), s => bail!("Unknown enum meta {}", s), } } pub fn find_nested_meta<'a>(meta: &'a MetaList, name: &str) -> Option<&'a NestedMeta> { meta.nested.iter().find(|n| match n { NestedMeta::Meta(m) => m.path().is_ident(name), _ => false, }) } // Parse attribute such as: // #[genum(type_name = "TestAnimalType")] pub fn parse_type_name(input: &DeriveInput, attr_name: &str) -> Result { let meta = match find_attribute_meta(&input.attrs, attr_name)? { Some(meta) => meta, _ => bail!("Missing '{}' attribute", attr_name), }; let meta = match find_nested_meta(&meta, "type_name") { Some(meta) => meta, _ => bail!("Missing meta 'type_name'"), }; match parse_enum_attribute(meta)? { EnumAttribute::TypeName(n) => Ok(n), } } #[derive(Debug)] pub enum ErrorDomainAttribute { Name(String), } pub fn parse_error_attribute(meta: &NestedMeta) -> Result { let (ident, v) = parse_attribute(meta)?; match ident.as_ref() { "name" => Ok(ErrorDomainAttribute::Name(v)), s => bail!("Unknown enum meta {}", s), } } // Parse attribute such as: // #[gerror_domain(name = "MyError")] pub fn parse_name(input: &DeriveInput, attr_name: &str) -> Result { let meta = match find_attribute_meta(&input.attrs, attr_name)? { Some(meta) => meta, _ => bail!("Missing '{}' attribute", attr_name), }; let meta = match find_nested_meta(&meta, "name") { Some(meta) => meta, _ => bail!("Missing meta 'name'"), }; match parse_error_attribute(meta)? { ErrorDomainAttribute::Name(n) => Ok(n), } } #[derive(Debug)] pub enum ItemAttribute { Name(String), Nick(String), } fn parse_item_attribute(meta: &NestedMeta) -> Result { let (ident, v) = parse_attribute(meta)?; match ident.as_ref() { "name" => Ok(ItemAttribute::Name(v)), "nick" => Ok(ItemAttribute::Nick(v)), s => bail!("Unknown item meta {}", s), } } // Parse optional enum item attributes such as: // #[genum(name = "My Name", nick = "my-nick")] pub fn parse_item_attributes(attr_name: &str, attrs: &[Attribute]) -> Result> { let meta = find_attribute_meta(attrs, attr_name)?; let v = match meta { Some(meta) => meta .nested .iter() .map(|m| parse_item_attribute(m)) .collect::, _>>()?, None => Vec::new(), }; Ok(v) } 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 Some(#enum_name::#name); } } }); quote! { #(#recurse)* None } } glib-macros-0.14.1/tests/test.rs000064400000000000000000000214320000000000000145540ustar 00000000000000// Take a look at the license at the top of the repository in the LICENSE file. use glib::prelude::*; use glib::translate::{FromGlib, IntoGlib}; use glib::{gflags, GBoxed, GEnum, GErrorDomain, GSharedBoxed}; #[test] fn derive_gerror_domain() { #[derive(Debug, Eq, PartialEq, Clone, Copy, GErrorDomain)] #[gerror_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, GSharedBoxed)] #[gshared_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, GSharedBoxed)] #[gshared_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_genum() { #[derive(Debug, Eq, PartialEq, Clone, Copy, GEnum)] #[repr(u32)] #[genum(type_name = "TestAnimalType")] enum Animal { Goat, #[genum(name = "The Dog")] Dog, #[genum(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::new(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_gboxed() { #[derive(Clone, Debug, PartialEq, Eq, GBoxed)] #[gboxed(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()); } #[test] fn derive_gboxed_nullable() { #[derive(Clone, Debug, PartialEq, Eq, GBoxed)] #[gboxed(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_gflags() { #[gflags("MyFlags")] enum MyFlags { #[gflags(name = "Flag A", nick = "nick-a")] A = 0b00000001, #[gflags(name = "Flag B")] B = 0b00000010, #[gflags(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::new(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 super::*; use glib::subclass::prelude::*; 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; type ParentType = glib::Object; } impl ObjectImpl for Foo {} } pub trait FooExt: 'static { fn test(&self); } impl> FooExt for O { fn test(&self) { let _self = imp::Foo::from_instance(self.as_ref().downcast_ref::().unwrap()); unimplemented!() } } glib::wrapper! { pub struct Foo(ObjectSubclass); } } }