Skip to content
This repository was archived by the owner on Mar 4, 2024. It is now read-only.

Commit fb54c7f

Browse files
authored
Merge pull request #452 from mbiggio/wip/biggio/shared-macro
glib: glib-macros: add SharedType and Shared derive macro
2 parents 3b154eb + 3c24a50 commit fb54c7f

File tree

21 files changed

+530
-16
lines changed

21 files changed

+530
-16
lines changed

.github/workflows/CI.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- stable
1818
- beta
1919
- nightly
20-
- "1.48.0"
20+
- "1.51.0"
2121
conf:
2222
- { name: "atk", features: "v2_34", nightly: "--all-features", test_sys: true }
2323
- { name: "cairo", features: "png,pdf,svg,ps,use_glib,v1_16,freetype,script,xcb,xlib,win32-surface", nightly: "--features 'png,pdf,svg,ps,use_glib,v1_16,freetype,script,xcb,xlib,win32-surface'", test_sys: true }
@@ -119,7 +119,7 @@ jobs:
119119
- stable
120120
- beta
121121
- nightly
122-
- "1.48.0"
122+
- "1.51.0"
123123
steps:
124124
- uses: actions/checkout@v2
125125
- uses: actions-rs/toolchain@v1

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ information about each crate, please refer to their `README.md` file in their di
88

99
## Minimum supported Rust version
1010

11-
Currently, the minimum supported Rust version is `1.48.0`.
11+
Currently, the minimum supported Rust version is `1.51.0`.
1212

1313
## Documentation
1414

atk/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ __Rust__ bindings and wrappers for __Atk__, part of [gtk-rs](https://github.com/
55

66
## Minimum supported Rust version
77

8-
Currently, the minimum supported Rust version is `1.48.0`.
8+
Currently, the minimum supported Rust version is `1.51.0`.
99

1010
## Documentation
1111

cairo/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ __Rust__ bindings for Rust and wrappers for __Cairo__.
66

77
## Minimum supported Rust version
88

9-
Currently, the minimum supported Rust version is `1.48.0`.
9+
Currently, the minimum supported Rust version is `1.51.0`.
1010

1111
## Documentation
1212

gdk-pixbuf/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ __Rust__ bindings and wrappers for __Gdk-Pixbuf__.
44

55
## Minimum supported Rust version
66

7-
Currently, the minimum supported Rust version is `1.48.0`.
7+
Currently, the minimum supported Rust version is `1.51.0`.
88

99
## Documentation
1010

gdk/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ __Rust__ bindings and wrappers for __GDK__, part of [gtk-rs](https://github.com/
44

55
## Minimum supported Rust version
66

7-
Currently, the minimum supported Rust version is `1.48.0`.
7+
Currently, the minimum supported Rust version is `1.51.0`.
88

99
## Documentation
1010

gdkx11/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ __Rust__ bindings and wrappers for __GDKX11__, part of [gtk-rs](https://github.c
44

55
## Minimum supported Rust version
66

7-
Currently, the minimum supported Rust version is `1.48.0`.
7+
Currently, the minimum supported Rust version is `1.51.0`.
88

99
## Documentation
1010

gio/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ __Rust__ bindings and wrappers for __GIO__, part of [gtk-rs](https://github.com/
44

55
## Minimum supported Rust version
66

7-
Currently, the minimum supported Rust version is `1.48.0`.
7+
Currently, the minimum supported Rust version is `1.51.0`.
88

99
## Documentation
1010

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
3+
use crate::utils::{crate_ident_new, find_attribute_meta, find_nested_meta, parse_type_name};
4+
use proc_macro2::{Ident, TokenStream};
5+
use proc_macro_error::abort_call_site;
6+
use quote::quote;
7+
8+
fn gen_impl_set_value_optional(name: &Ident, crate_ident: &Ident) -> TokenStream {
9+
let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident);
10+
11+
quote! {
12+
impl #crate_ident::value::SetValueOptional for #name {
13+
unsafe fn set_value_optional(value: &mut #crate_ident::value::Value, this: Option<&Self>) {
14+
let ptr = match this {
15+
Some(this) => #refcounted_type_prefix::into_raw(this.0.clone()),
16+
None => std::ptr::null(),
17+
};
18+
19+
#crate_ident::gobject_ffi::g_value_take_boxed(
20+
#crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(value).0,
21+
ptr as *mut _,
22+
);
23+
}
24+
}
25+
}
26+
}
27+
28+
fn gen_impl_from_value(name: &Ident, crate_ident: &Ident) -> TokenStream {
29+
let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident);
30+
31+
quote! {
32+
impl<'a> #crate_ident::value::FromValue<'a> for #name {
33+
unsafe fn from_value(value: &'a #crate_ident::value::Value) -> Self {
34+
let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(
35+
#crate_ident::translate::ToGlibPtr::to_glib_none(value).0,
36+
);
37+
assert!(!ptr.is_null());
38+
#name(#refcounted_type_prefix::from_raw(ptr as *mut _))
39+
}
40+
}
41+
}
42+
}
43+
44+
fn gen_ptr_to_option(name: &Ident, nullable: bool, crate_ident: &Ident) -> TokenStream {
45+
let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident);
46+
47+
if nullable {
48+
quote! {
49+
if ptr.is_null() {
50+
None
51+
} else {
52+
Some(#name(#refcounted_type_prefix::from_raw(ptr as *mut _)))
53+
}
54+
}
55+
} else {
56+
quote! {
57+
assert!(!ptr.is_null());
58+
Some(#name(#refcounted_type_prefix::from_raw(ptr as *mut _)))
59+
}
60+
}
61+
}
62+
63+
fn refcounted_type(input: &syn::DeriveInput) -> Option<&syn::TypePath> {
64+
let fields = match &input.data {
65+
syn::Data::Struct(s) => &s.fields,
66+
_ => return None,
67+
};
68+
69+
let unnamed = match fields {
70+
syn::Fields::Unnamed(u) if u.unnamed.len() == 1 => &u.unnamed[0],
71+
_ => return None,
72+
};
73+
74+
let refcounted = match &unnamed.ty {
75+
syn::Type::Path(p) => p,
76+
_ => return None,
77+
};
78+
79+
Some(refcounted)
80+
}
81+
82+
fn refcounted_type_prefix(name: &Ident, crate_ident: &Ident) -> proc_macro2::TokenStream {
83+
quote! {
84+
<<#name as #crate_ident::subclass::shared::SharedType>::RefCountedType as #crate_ident::subclass::shared::RefCounted>
85+
}
86+
}
87+
88+
pub fn impl_gshared_boxed(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
89+
let refcounted_type = match refcounted_type(input) {
90+
Some(p) => p,
91+
_ => abort_call_site!("derive(GSharedBoxed) requires struct MyStruct(T: RefCounted)"),
92+
};
93+
94+
let name = &input.ident;
95+
let gtype_name = match parse_type_name(&input, "gshared_boxed") {
96+
Ok(v) => v,
97+
Err(e) => abort_call_site!(
98+
"{}: derive(GSharedBoxed) requires #[gshared_boxed(type_name = \"SharedTypeName\")]",
99+
e
100+
),
101+
};
102+
103+
let meta = find_attribute_meta(&input.attrs, "gshared_boxed")
104+
.unwrap()
105+
.unwrap();
106+
let nullable = find_nested_meta(&meta, "nullable").is_some();
107+
let crate_ident = crate_ident_new();
108+
let refcounted_type_prefix = refcounted_type_prefix(name, &crate_ident);
109+
let ptr_to_option = gen_ptr_to_option(name, nullable, &crate_ident);
110+
111+
let impl_from_value = if !nullable {
112+
gen_impl_from_value(name, &crate_ident)
113+
} else {
114+
quote! {}
115+
};
116+
117+
let impl_set_value_optional = if nullable {
118+
gen_impl_set_value_optional(name, &crate_ident)
119+
} else {
120+
quote! {}
121+
};
122+
123+
quote! {
124+
impl #crate_ident::subclass::shared::SharedType for #name {
125+
const NAME: &'static str = #gtype_name;
126+
127+
type RefCountedType = #refcounted_type;
128+
129+
fn get_type() -> #crate_ident::Type {
130+
static mut TYPE_: #crate_ident::Type = #crate_ident::Type::INVALID;
131+
static ONCE: ::std::sync::Once = ::std::sync::Once::new();
132+
133+
ONCE.call_once(|| {
134+
let type_ = #crate_ident::subclass::shared::register_shared_type::<Self>();
135+
unsafe {
136+
TYPE_ = type_;
137+
}
138+
});
139+
140+
unsafe { TYPE_ }
141+
}
142+
143+
fn from_refcounted(this: Self::RefCountedType) -> Self {
144+
Self(this)
145+
}
146+
147+
fn into_refcounted(self) -> Self::RefCountedType {
148+
self.0
149+
}
150+
}
151+
152+
impl #crate_ident::StaticType for #name {
153+
fn static_type() -> #crate_ident::Type {
154+
<#name as #crate_ident::subclass::shared::SharedType>::get_type()
155+
}
156+
}
157+
158+
impl #crate_ident::value::SetValue for #name {
159+
unsafe fn set_value(value: &mut #crate_ident::value::Value, this: &Self) {
160+
let ptr = #refcounted_type_prefix::into_raw(this.0.clone());
161+
#crate_ident::gobject_ffi::g_value_take_boxed(
162+
#crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(value).0,
163+
ptr as *mut _,
164+
);
165+
}
166+
}
167+
168+
#impl_set_value_optional
169+
170+
impl<'a> #crate_ident::value::FromValueOptional<'a> for #name {
171+
unsafe fn from_value_optional(value: &'a #crate_ident::value::Value) -> Option<Self> {
172+
let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(
173+
#crate_ident::translate::ToGlibPtr::to_glib_none(value).0,
174+
);
175+
#ptr_to_option
176+
}
177+
}
178+
179+
#impl_from_value
180+
}
181+
}

glib-macros/src/lib.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
mod clone;
44
mod downgrade_derive;
55
mod gboxed_derive;
6+
mod gboxed_shared_derive;
67
mod genum_derive;
78
mod gerror_domain_derive;
89
mod gflags_attribute;
@@ -300,6 +301,34 @@ pub fn gboxed_derive(input: TokenStream) -> TokenStream {
300301
gen.into()
301302
}
302303

304+
/// Derive macro for defining a [`SharedType`]`::get_type` function and
305+
/// the [`glib::Value`] traits.
306+
///
307+
/// # Example
308+
///
309+
/// ```
310+
/// use glib::prelude::*;
311+
/// use glib::subclass::prelude::*;
312+
///
313+
/// #[derive(Clone, Debug, PartialEq, Eq)]
314+
/// struct MySharedInner {
315+
/// foo: String,
316+
/// }
317+
/// #[derive(Clone, Debug, PartialEq, Eq, glib::GSharedBoxed)]
318+
/// #[gshared_boxed(type_name = "MyShared")]
319+
/// struct MyShared(std::sync::Arc<MySharedInner>);
320+
/// ```
321+
///
322+
/// [`SharedType`]: subclass/shared/trait.SharedType.html
323+
/// [`glib::Value`]: value/struct.Value.html
324+
#[proc_macro_derive(GSharedBoxed, attributes(gshared_boxed))]
325+
#[proc_macro_error]
326+
pub fn gshared_boxed_derive(input: TokenStream) -> TokenStream {
327+
let input = parse_macro_input!(input as DeriveInput);
328+
let gen = gboxed_shared_derive::impl_gshared_boxed(&input);
329+
gen.into()
330+
}
331+
303332
/// Attribute macro for defining flags using the `bitflags` crate.
304333
/// This macro will also define a `GFlags::get_type` function and
305334
/// the [`glib::Value`] traits.

glib-macros/tests/test.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use glib::prelude::*;
66
use glib::subclass::prelude::*;
77
use glib::translate::{FromGlib, ToGlib};
8-
use glib::{gflags, GBoxed, GEnum, GErrorDomain};
8+
use glib::{gflags, GBoxed, GEnum, GErrorDomain, GSharedBoxed};
99

1010
#[test]
1111
fn derive_gerror_domain() {
@@ -22,6 +22,72 @@ fn derive_gerror_domain() {
2222
assert!(matches!(err.kind::<TestError>(), Some(TestError::Bad)));
2323
}
2424

25+
#[test]
26+
fn derive_shared_arc() {
27+
#[derive(Debug, Eq, PartialEq, Clone)]
28+
struct MyInnerShared {
29+
foo: String,
30+
}
31+
#[derive(Debug, Eq, PartialEq, Clone, GSharedBoxed)]
32+
#[gshared_boxed(type_name = "MySharedType")]
33+
struct MyShared(std::sync::Arc<MyInnerShared>);
34+
35+
assert_eq!(MyShared::get_type().name(), "MySharedType");
36+
37+
let p = MyShared(std::sync::Arc::new(MyInnerShared {
38+
foo: String::from("bar"),
39+
}));
40+
41+
assert_eq!(std::sync::Arc::strong_count(&p.0), 1);
42+
let v = p.to_value();
43+
assert_eq!(std::sync::Arc::strong_count(&p.0), 2);
44+
let p_clone = v.get::<MyShared>().unwrap().unwrap();
45+
assert_eq!(std::sync::Arc::strong_count(&p.0), 3);
46+
drop(p_clone);
47+
assert_eq!(std::sync::Arc::strong_count(&p.0), 2);
48+
drop(v);
49+
assert_eq!(std::sync::Arc::strong_count(&p.0), 1);
50+
}
51+
52+
#[test]
53+
fn derive_shared_arc_nullable() {
54+
#[derive(Debug, Eq, PartialEq, Clone)]
55+
struct MyInnerNullableShared {
56+
foo: String,
57+
}
58+
#[derive(Clone, Debug, PartialEq, Eq, GSharedBoxed)]
59+
#[gshared_boxed(type_name = "MyNullableSharedType", nullable)]
60+
struct MyNullableShared(std::sync::Arc<MyInnerNullableShared>);
61+
62+
assert_eq!(MyNullableShared::get_type().name(), "MyNullableSharedType");
63+
64+
let p = MyNullableShared(std::sync::Arc::new(MyInnerNullableShared {
65+
foo: String::from("bar"),
66+
}));
67+
68+
assert_eq!(std::sync::Arc::strong_count(&p.0), 1);
69+
let _v = p.to_value();
70+
assert_eq!(std::sync::Arc::strong_count(&p.0), 2);
71+
72+
let p = Some(MyNullableShared(std::sync::Arc::new(
73+
MyInnerNullableShared {
74+
foo: String::from("foo"),
75+
},
76+
)));
77+
78+
assert_eq!(std::sync::Arc::strong_count(&p.as_ref().unwrap().0), 1);
79+
let v = p.to_value();
80+
assert_eq!(std::sync::Arc::strong_count(&p.as_ref().unwrap().0), 2);
81+
assert_eq!(
82+
p.as_ref().unwrap().0.foo,
83+
v.get::<MyNullableShared>().unwrap().unwrap().0.foo
84+
);
85+
86+
let b: Option<MyNullableShared> = None;
87+
let v = b.to_value();
88+
assert_eq!(None, v.get::<MyNullableShared>().unwrap());
89+
}
90+
2591
#[test]
2692
fn derive_genum() {
2793
#[derive(Debug, Eq, PartialEq, Clone, Copy, GEnum)]

glib/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ __Rust__ bindings and wrappers for __GLib__, part of [gtk-rs](https://github.com
44

55
## Minimum supported Rust version
66

7-
Currently, the minimum supported Rust version is `1.48.0`.
7+
Currently, the minimum supported Rust version is `1.51.0`.
88

99
## Documentation
1010

0 commit comments

Comments
 (0)