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

glib: glib-macros: add SharedType and Shared derive macro #452

Merged
merged 3 commits into from
Apr 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- stable
- beta
- nightly
- "1.48.0"
- "1.51.0"
conf:
- { name: "atk", features: "v2_34", nightly: "--all-features", test_sys: true }
- { 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 }
Expand Down Expand Up @@ -119,7 +119,7 @@ jobs:
- stable
- beta
- nightly
- "1.48.0"
- "1.51.0"
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ information about each crate, please refer to their `README.md` file in their di

## Minimum supported Rust version

Currently, the minimum supported Rust version is `1.48.0`.
Currently, the minimum supported Rust version is `1.51.0`.

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion atk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ __Rust__ bindings and wrappers for __Atk__, part of [gtk-rs](https://github.com/

## Minimum supported Rust version

Currently, the minimum supported Rust version is `1.48.0`.
Currently, the minimum supported Rust version is `1.51.0`.

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion cairo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ __Rust__ bindings for Rust and wrappers for __Cairo__.

## Minimum supported Rust version

Currently, the minimum supported Rust version is `1.48.0`.
Currently, the minimum supported Rust version is `1.51.0`.

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion gdk-pixbuf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ __Rust__ bindings and wrappers for __Gdk-Pixbuf__.

## Minimum supported Rust version

Currently, the minimum supported Rust version is `1.48.0`.
Currently, the minimum supported Rust version is `1.51.0`.

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion gdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ __Rust__ bindings and wrappers for __GDK__, part of [gtk-rs](https://github.com/

## Minimum supported Rust version

Currently, the minimum supported Rust version is `1.48.0`.
Currently, the minimum supported Rust version is `1.51.0`.

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion gdkx11/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ __Rust__ bindings and wrappers for __GDKX11__, part of [gtk-rs](https://github.c

## Minimum supported Rust version

Currently, the minimum supported Rust version is `1.48.0`.
Currently, the minimum supported Rust version is `1.51.0`.

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion gio/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ __Rust__ bindings and wrappers for __GIO__, part of [gtk-rs](https://github.com/

## Minimum supported Rust version

Currently, the minimum supported Rust version is `1.48.0`.
Currently, the minimum supported Rust version is `1.51.0`.

## Documentation

Expand Down
181 changes: 181 additions & 0 deletions glib-macros/src/gboxed_shared_derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Take a look at the license at the top of the repository in the LICENSE file.

use crate::utils::{crate_ident_new, find_attribute_meta, find_nested_meta, parse_type_name};
use proc_macro2::{Ident, TokenStream};
use proc_macro_error::abort_call_site;
use quote::quote;

fn gen_impl_set_value_optional(name: &Ident, crate_ident: &Ident) -> TokenStream {
let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident);

quote! {
impl #crate_ident::value::SetValueOptional for #name {
unsafe fn set_value_optional(value: &mut #crate_ident::value::Value, this: Option<&Self>) {
let ptr = match this {
Some(this) => #refcounted_type_prefix::into_raw(this.0.clone()),
None => std::ptr::null(),
};

#crate_ident::gobject_ffi::g_value_take_boxed(
#crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(value).0,
ptr as *mut _,
);
}
}
}
}

fn gen_impl_from_value(name: &Ident, crate_ident: &Ident) -> TokenStream {
let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident);

quote! {
impl<'a> #crate_ident::value::FromValue<'a> for #name {
unsafe fn from_value(value: &'a #crate_ident::value::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_ptr_to_option(name: &Ident, nullable: bool, crate_ident: &Ident) -> TokenStream {
let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident);

if nullable {
quote! {
if ptr.is_null() {
None
} else {
Some(#name(#refcounted_type_prefix::from_raw(ptr as *mut _)))
}
}
} else {
quote! {
assert!(!ptr.is_null());
Some(#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: &Ident) -> 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 ptr_to_option = gen_ptr_to_option(name, nullable, &crate_ident);

let impl_from_value = if !nullable {
gen_impl_from_value(name, &crate_ident)
} else {
quote! {}
};

let impl_set_value_optional = if nullable {
gen_impl_set_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 get_type() -> #crate_ident::Type {
static mut TYPE_: #crate_ident::Type = #crate_ident::Type::INVALID;
static ONCE: ::std::sync::Once = ::std::sync::Once::new();

ONCE.call_once(|| {
let type_ = #crate_ident::subclass::shared::register_shared_type::<Self>();
unsafe {
TYPE_ = type_;
}
});

unsafe { 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 {
<#name as #crate_ident::subclass::shared::SharedType>::get_type()
}
}

impl #crate_ident::value::SetValue for #name {
unsafe fn set_value(value: &mut #crate_ident::value::Value, this: &Self) {
let ptr = #refcounted_type_prefix::into_raw(this.0.clone());
#crate_ident::gobject_ffi::g_value_take_boxed(
#crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(value).0,
ptr as *mut _,
);
}
}

#impl_set_value_optional

impl<'a> #crate_ident::value::FromValueOptional<'a> for #name {
unsafe fn from_value_optional(value: &'a #crate_ident::value::Value) -> Option<Self> {
let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(
#crate_ident::translate::ToGlibPtr::to_glib_none(value).0,
);
#ptr_to_option
}
}

#impl_from_value
}
}
29 changes: 29 additions & 0 deletions glib-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod clone;
mod downgrade_derive;
mod gboxed_derive;
mod gboxed_shared_derive;
mod genum_derive;
mod gerror_domain_derive;
mod gflags_attribute;
Expand Down Expand Up @@ -300,6 +301,34 @@ pub fn gboxed_derive(input: TokenStream) -> TokenStream {
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<MySharedInner>);
/// ```
///
/// [`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::get_type` function and
/// the [`glib::Value`] traits.
Expand Down
68 changes: 67 additions & 1 deletion glib-macros/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use glib::prelude::*;
use glib::subclass::prelude::*;
use glib::translate::{FromGlib, ToGlib};
use glib::{gflags, GBoxed, GEnum, GErrorDomain};
use glib::{gflags, GBoxed, GEnum, GErrorDomain, GSharedBoxed};

#[test]
fn derive_gerror_domain() {
Expand All @@ -22,6 +22,72 @@ fn derive_gerror_domain() {
assert!(matches!(err.kind::<TestError>(), 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 = "MySharedType")]
struct MyShared(std::sync::Arc<MyInnerShared>);

assert_eq!(MyShared::get_type().name(), "MySharedType");

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::<MyShared>().unwrap().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 = "MyNullableSharedType", nullable)]
struct MyNullableShared(std::sync::Arc<MyInnerNullableShared>);

assert_eq!(MyNullableShared::get_type().name(), "MyNullableSharedType");

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::<MyNullableShared>().unwrap().unwrap().0.foo
);

let b: Option<MyNullableShared> = None;
let v = b.to_value();
assert_eq!(None, v.get::<MyNullableShared>().unwrap());
}

#[test]
fn derive_genum() {
#[derive(Debug, Eq, PartialEq, Clone, Copy, GEnum)]
Expand Down
2 changes: 1 addition & 1 deletion glib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ __Rust__ bindings and wrappers for __GLib__, part of [gtk-rs](https://github.com

## Minimum supported Rust version

Currently, the minimum supported Rust version is `1.48.0`.
Currently, the minimum supported Rust version is `1.51.0`.

## Documentation

Expand Down
Loading