Skip to content
This repository was archived by the owner on Jun 8, 2021. It is now read-only.
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
190 changes: 190 additions & 0 deletions src/clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
use std::rc::{self, Rc};
use std::sync::{self, Arc};

/// Trait for generalizing downgrading a strong reference to a weak reference.
pub trait Downgrade
where
Self: Sized,
{
/// Weak reference type.
type Weak;

/// Downgrade to a weak reference.
fn downgrade(&self) -> Self::Weak;
}

/// Trait for generalizing upgrading a weak reference to a strong reference.
pub trait Upgrade
where
Self: Sized,
{
/// Strong reference type.
type Strong;

/// Try upgrading a weak reference to a strong reference.
fn upgrade(&self) -> Option<Self::Strong>;
}

impl<T: Downgrade + crate::ObjectType> Upgrade for crate::WeakRef<T> {
type Strong = T;

fn upgrade(&self) -> Option<Self::Strong> {
self.upgrade()
}
}

impl<T> Downgrade for Arc<T> {
type Weak = sync::Weak<T>;

fn downgrade(&self) -> Self::Weak {
Arc::downgrade(self)
}
}

impl<T> Upgrade for sync::Weak<T> {
type Strong = Arc<T>;

fn upgrade(&self) -> Option<Self::Strong> {
self.upgrade()
}
}

impl<T> Downgrade for Rc<T> {
type Weak = rc::Weak<T>;

fn downgrade(&self) -> Self::Weak {
Rc::downgrade(self)
}
}

impl<T> Upgrade for rc::Weak<T> {
type Strong = Rc<T>;

fn upgrade(&self) -> Option<Self::Strong> {
self.upgrade()
}
}

#[doc(hidden)]
#[macro_export]
macro_rules! to_type_before {
(_) => ();
(self) => (
compile_error!("Can't use `self` as variable name for the `clone!` macro. Try storing it in a temporary variable.");
);
(@weak self) => (
compile_error!("Can't use `self` as variable name for the `clone!` macro. Try storing it in a temporary variable.");
);
($variable:ident) => (
let $variable = $variable.clone();
);
(@weak $variable:ident) => (
let $variable = $crate::clone::Downgrade::downgrade(&$variable);
);
}

#[doc(hidden)]
#[macro_export]
macro_rules! to_type_after {
(_ , $return_value:expr) => {};
(self, $return_value:expr) => {};
(@weak self, $return_value:expr) => {};
($variable:ident , $return_value:expr) => {};
(@weak $variable:ident , $return_value:expr) => {
let $variable = match $crate::clone::Upgrade::upgrade(&$variable) {
Some(val) => val,
None => return ($return_value)(),
};
};
}

#[doc(hidden)]
#[macro_export]
macro_rules! to_return_value {
() => {
()
};
($value:expr) => {
$value
};
}

/// 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.
///
/// ### Passing a strong reference
///
/// ```
/// use glib::clone;
/// use std::rc::Rc;
///
/// let v = Rc::new(1);
/// let closure = clone!(v => move |x| {
/// println!("v: {}, x: {}", v, x);
/// });
///
/// closure(2);
/// ```
///
/// ### Passing a strong and weak reference
///
/// ```
/// use glib::clone;
/// use std::rc::Rc;
///
/// let v = Rc::new(1);
/// let u = Rc::new(2);
/// let closure = clone!(v, @weak u => move |x| {
/// println!("v: {}, u: {}, x: {}", v, u, x);
/// });
///
/// closure(3);
/// ```
///
/// ### Providing a default return value if upgrading a weak reference fails
///
/// ```
/// use glib::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);
/// ```
#[macro_export]
macro_rules! clone {
($($(@ $weak:ident)? $variables:ident),+ => $(@default-return $return_value:expr,)? move || $body:block ) => (
{
$( $crate::to_type_before!($(@ $weak)? $variables); )*
move || {
let return_value = || $crate::to_return_value!($($return_value)?);
$( $crate::to_type_after!($(@ $weak)? $variables, return_value );)*
$body
}
}
);
($($(@ $weak:ident)? $variables:ident),+ => $(@default-return $return_value:expr ,)? move | $($pattern:pat),* | $body:block ) => (
{
$( $crate::to_type_before!($(@ $weak)? $variables); )*
move |$($pattern),*| {
let return_value = || $crate::to_return_value!($($return_value)?);
$( $crate::to_type_after!($(@ $weak)? $variables, return_value );)*
$body
}
}
);
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ pub use value::{SendValue, ToSendValue, ToValue, TypedValue, Value};
pub use variant::{StaticVariantType, ToVariant, Variant};
pub use variant_type::{VariantTy, VariantType};

#[macro_use]
pub mod clone;
#[macro_use]
pub mod wrapper;
#[macro_use]
Expand Down
17 changes: 17 additions & 0 deletions src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,21 @@ glib_wrapper! {
}
}

#[doc(hidden)]
#[macro_export]
macro_rules! glib_weak_impl {
($name:ident) => {
#[doc(hidden)]
impl $crate::clone::Downgrade for $name {
type Weak = $crate::object::WeakRef<Self>;

fn downgrade(&self) -> Self::Weak {
<Self as $crate::object::ObjectExt>::downgrade(&self)
}
}
};
}

/// ObjectType implementations for Object types. See `glib_wrapper!`.
#[macro_export]
macro_rules! glib_object_wrapper {
Expand Down Expand Up @@ -740,6 +755,8 @@ macro_rules! glib_object_wrapper {
$crate::gobject_sys::g_value_set_object($crate::translate::ToGlibPtrMut::to_glib_none_mut(value).0, $crate::translate::ToGlibPtr::<*mut $ffi_name>::to_glib_none(&this).0 as *mut $crate::gobject_sys::GObject)
}
}

$crate::glib_weak_impl!($name);
};

(@munch_impls $name:ident, ) => { };
Expand Down
93 changes: 93 additions & 0 deletions tests/clone.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
extern crate glib;

use std::cell::RefCell;
use std::panic;
use std::rc::Rc;
use std::sync::{Arc, Mutex};

use glib::clone;

struct State {
count: i32,
started: bool,
}

impl State {
fn new() -> Self {
Self {
count: 0,
started: false,
}
}
}

#[test]
fn clone_closure() {
let state = Rc::new(RefCell::new(State::new()));
assert_eq!(state.borrow().started, false);

let closure = {
clone!(@weak state => move || {
state.borrow_mut().started = true;
})
};

assert_eq!(closure(), ());

assert_eq!(state.borrow().started, true);
assert_eq!(state.borrow().count, 0);

let closure = {
let state2 = Rc::new(RefCell::new(State::new()));
assert_eq!(state.borrow().started, true);

clone!(@weak state, state2 => move || {
state.borrow_mut().count += 1;
state.borrow_mut().started = true;
state2.borrow_mut().started = true;
})
};

assert_eq!(closure(), ());

assert_eq!(state.borrow().count, 1);
assert_eq!(state.borrow().started, true);
}

#[test]
fn clone_default_value() {
let closure = {
let state = Rc::new(RefCell::new(State::new()));
clone!(@weak state => @default-return 42, move |_| {
state.borrow_mut().started = true;
10
})
};

assert_eq!(42, closure(50));
}

#[test]
fn clone_panic() {
let state = Arc::new(Mutex::new(State::new()));
state.lock().unwrap().count = 20;

let closure = {
let state2 = Arc::new(Mutex::new(State::new()));
clone!(@weak state2, state => @default-return panic!(), move |_| {
state.lock().unwrap().count = 21;
state2.lock().unwrap().started = true;
10
})
};

let result = panic::catch_unwind(|| {
closure(50);
});

if result.is_ok() {
assert!(false, "should panic");
}

assert_eq!(state.lock().unwrap().count, 20);
}