use std::rc::{self, Rc};
use std::sync::{self, Arc};
pub trait Downgrade
where
Self: Sized,
{
type Weak;
fn downgrade(&self) -> Self::Weak;
}
pub trait Upgrade
where
Self: Sized,
{
type Strong;
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> Downgrade for &T {
type Weak = T::Weak;
fn downgrade(&self) -> Self::Weak {
T::downgrade(*self)
}
}
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 {
(_) => ();
($($variable:ident).+ $(as $rename:ident)?) => (
compile_error!("You need to specify if this is a weak or a strong clone.");
);
(@strong $variable:ident) => (
let $variable = $variable.clone();
);
(@weak $variable:ident) => (
let $variable = $crate::clone::Downgrade::downgrade(&$variable);
);
(@strong $($variable:ident).+ as $rename:ident) => (
let $rename = $($variable).+.clone();
);
(@weak $($variable:ident).+ as $rename:ident) => (
let $rename = $crate::clone::Downgrade::downgrade(&$($variable).+);
);
(@ $keyword:ident $($variable:ident).+ $(as $rename:ident)?) => (
compile_error!("Unknown keyword, only `weak` and `strong` are allowed");
);
}
#[doc(hidden)]
#[macro_export]
macro_rules! to_type_after {
(@default-panic, @weak $variable:ident) => {
let $variable = match $crate::clone::Upgrade::upgrade(&$variable) {
Some(val) => val,
None => panic!("failed to upgrade {}", stringify!($variable)),
};
};
(as $rename:ident @default-panic, @weak $($variable:ident).+) => {
let $rename = match $crate::clone::Upgrade::upgrade(&$rename) {
Some(val) => val,
None => panic!("failed to upgrade {}", stringify!($rename)),
};
};
($(as $rename:ident)? @default-panic, @strong $($variable:ident).+) => {};
(@weak $variable:ident , $return_value:expr) => {
let $variable = match $crate::clone::Upgrade::upgrade(&$variable) {
Some(val) => val,
None => return ($return_value)(),
};
};
(as $rename:ident @weak $($variable:ident).+ , $return_value:expr) => {
let $rename = match $crate::clone::Upgrade::upgrade(&$rename) {
Some(val) => val,
None => return ($return_value)(),
};
};
($(as $rename:ident)? @strong $($variable:ident).+ , $return_value:expr) => {};
($(as $rename:ident)? @ $keyword:ident $($variable:ident).+, $return_value:expr) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! to_return_value {
() => {
()
};
($value:expr) => {
$value
};
}
#[macro_export]
macro_rules! clone {
( => $($_:tt)*) => (
compile_error!("If you have nothing to clone, no need to use this macro!");
);
($(move)? || $($_:tt)*) => (
compile_error!("If you have nothing to clone, no need to use this macro!");
);
($(move)? | $($arg:tt $(: $typ:ty)?),* | $($_:tt)*) => (
compile_error!("If you have nothing to clone, no need to use this macro!")
);
($($(@ $strength:ident)? self),+ => $($_:tt)* ) => (
compile_error!("Can't use `self` as variable name. Try storing it in a temporary variable or rename it using `as`.");
);
($($(@ $strength:ident)? $up:ident.$($variables:ident).+),+ => $($_:tt)* ) => (
compile_error!("Field accesses are not allowed as is, you must rename it!");
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => @default-panic, move || $body:block ) => (
{
$( $crate::to_type_before!($(@ $strength)? $($variables).+ $(as $rename)?); )*
move || {
$( $crate::to_type_after!($(as $rename)? @default-panic, $(@ $strength)? $($variables).+);)*
$body
}
}
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => @default-panic, move || $body:expr ) => (
clone!($($(@ $strength)? $($variables).+ $(as $rename)?),* => @default-panic, move || { $body })
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => $(@default-return $return_value:expr,)? move || $body:block ) => (
{
$( $crate::to_type_before!($(@ $strength)? $($variables).+ $(as $rename)?); )*
move || {
let _return_value = || $crate::to_return_value!($($return_value)?);
$( $crate::to_type_after!($(as $rename)? $(@ $strength)? $($variables).+, _return_value );)*
$body
}
}
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => $(@default-return $return_value:expr,)? move || $body:expr ) => (
clone!($($(@ $strength)? $($variables).+ $(as $rename)?),* => $(@default-return $return_value,)? move || { $body })
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => @default-panic, move | $($arg:tt $(: $typ:ty)?),* | $body:block ) => (
{
$( $crate::to_type_before!($(@ $strength)? $($variables).+ $(as $rename)?); )*
move |$($arg $(: $typ)?),*| {
$( $crate::to_type_after!($(as $rename)? @default-panic, $(@ $strength)? $($variables).+);)*
$body
}
}
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => @default-panic, move | $($arg:tt $(: $typ:ty)?),* | $body:expr ) => (
clone!($($(@ $strength)? $($variables).+ $(as $rename)?),* => @default-panic, move |$($arg $(: $typ)?),*| { $body })
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => $(@default-return $return_value:expr,)? move | $($arg:tt $(: $typ:ty)?),* | $body:block ) => (
{
$( $crate::to_type_before!($(@ $strength)? $($variables).+ $(as $rename)?); )*
move | $($arg $(: $typ)?),* | {
let _return_value = || $crate::to_return_value!($($return_value)?);
$( $crate::to_type_after!($(as $rename)? $(@ $strength)? $($variables).+, _return_value);)*
$body
}
}
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => $(@default-return $return_value:expr,)? move | $($arg:tt $(: $typ:ty)?),* | $body:expr ) => (
clone!($($(@ $strength)? $($variables).+ $(as $rename)?),+ => $(@default-return $return_value,)? move |$($arg $(: $typ)?),*| { $body })
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => @default-return $return_value:expr, || $body:block ) => (
compile_error!("Closure needs to be \"moved\" so please add `move` before closure");
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => @default-return $return_value:expr, | $($arg:tt $(: $typ:ty)?),* | $body:block ) => (
compile_error!("Closure needs to be \"moved\" so please add `move` before closure");
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => default-return $($x:tt)+ ) => (
compile_error!("Missing `@` before `default-return`");
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => @default-return $($x:tt)+ ) => (
compile_error!("Missing comma after `@default-return`'s value");
);
($($(@ $strength:ident)? $variables:expr),+ => $($_:tt)* ) => (
compile_error!("Variables need to be valid identifiers, e.g. field accesses are not allowed as is, you must rename it!");
);
}
#[cfg(test)]
mod tests {
#![allow(dead_code)]
#![allow(unused_variables)]
use std::rc::Rc;
#[test]
fn test_clone_macro_self_rename() {
#[derive(Debug)]
struct Foo {
v: u8,
}
impl Foo {
fn foo(&self) {
let closure = clone!(@strong self as this => move |_x| {
println!("v: {:?}", this);
});
closure(0i8);
let _ = clone!(@strong self as this => move || {
println!("v: {:?}", this);
});
let closure = clone!(@strong self as this => move |_x| println!("v: {:?}", this));
closure(0i8);
let _ = clone!(@strong self as this => move || println!("v: {:?}", this));
let closure = clone!(@strong self.v as v => move |_x| {
println!("v: {:?}", v);
});
closure(0i8);
let _ = clone!(@strong self.v as v => move || println!("v: {:?}", v));
let closure = clone!(@strong self.v as v => @default-panic, move |_x| {
println!("v: {:?}", v);
});
closure(0i8);
let _ =
clone!(@strong self.v as v => @default-panic, move || println!("v: {:?}", v));
let closure = clone!(@strong self.v as _v => @default-return true, move |_x| {
false
});
closure(0i8);
let _ = clone!(@strong self.v as _v => @default-return true, move || false);
}
}
}
#[test]
fn test_clone_macro_rename() {
let v = Rc::new(1);
let closure = clone!(@weak v as y => @default-panic, move |_x| {
println!("v: {}", y);
});
closure(0i8);
let _ = clone!(@weak v as y => @default-panic, move || println!("v: {}", y));
let closure = clone!(@strong v as y => @default-panic, move |_x| {
println!("v: {}", y);
});
closure(0i8);
let _ = clone!(@strong v as y => @default-panic, move || println!("v: {}", y));
let closure = clone!(@weak v as y => move |_x| {
println!("v: {}", y);
});
closure(0i8);
let _ = clone!(@weak v as y => move || println!("v: {}", y));
let closure = clone!(@strong v as y => move |_x| {
println!("v: {}", y);
});
closure(0i8);
let _ = clone!(@strong v as y => move || println!("v: {}", y));
let closure = clone!(@weak v as _y => @default-return true, move |_x| {
false
});
closure(0i8);
let _ = clone!(@weak v as _y => @default-return true, move || false);
let closure = clone!(@strong v as _y => @default-return true, move |_x| false);
closure(0i8);
let _ = clone!(@strong v as _y => @default-return true, move || false);
}
#[test]
fn test_clone_macro_simple() {
let v = Rc::new(1);
let closure = clone!(@weak v => @default-panic, move |_x| {
println!("v: {}", v);
});
closure(0i8);
let _ = clone!(@weak v => @default-panic, move || println!("v: {}", v));
let closure = clone!(@strong v => @default-panic, move |_x| {
println!("v: {}", v);
});
closure(0i8);
let _ = clone!(@strong v => @default-panic, move || println!("v: {}", v));
let closure = clone!(@weak v => move |_x| {
println!("v: {}", v);
});
closure(0i8);
let _ = clone!(@weak v => move || println!("v: {}", v));
let closure = clone!(@strong v => move |_x| {
println!("v: {}", v);
});
closure(0i8);
let _ = clone!(@strong v => move || println!("v: {}", v));
let closure = clone!(@weak v => @default-return true, move |_x| {
false
});
closure(0i8);
let _ = clone!(@weak v => @default-return true, move || false);
let closure = clone!(@strong v => @default-return true, move |_x| false);
closure(0i8);
let _ = clone!(@strong v => @default-return true, move || false);
}
#[test]
fn test_clone_macro_double_simple() {
let v = Rc::new(1);
let w = Rc::new(2);
let closure = clone!(@weak v, @weak w => @default-panic, move |_x| {
println!("v: {}, w: {}", v, w);
});
closure(0i8);
let _ = clone!(@weak v, @weak w => @default-panic, move || println!("v: {}, w: {}", v, w));
let closure = clone!(@strong v, @strong w => @default-panic, move |_x| {
println!("v: {}, w: {}", v, w);
});
closure(0i8);
let _ =
clone!(@strong v, @strong w => @default-panic, move || println!("v: {}, w: {}", v, w));
let closure = clone!(@weak v, @weak w => move |_x| {
println!("v: {}, w: {}", v, w);
});
closure(0i8);
let _ = clone!(@weak v, @weak w => move || println!("v: {}, w: {}", v, w));
let closure = clone!(@strong v, @strong w => move |_x| {
println!("v: {}, w: {}", v, w);
});
closure(0i8);
let _ = clone!(@strong v, @strong w => move || println!("v: {}, w: {}", v, w));
let closure = clone!(@weak v, @weak w => @default-return true, move |_x| {
false
});
closure(0i8);
let _ = clone!(@weak v, @weak w => @default-return true, move || false);
let closure = clone!(@strong v, @strong w => @default-return true, move |_x| false);
closure(0i8);
let _ = clone!(@strong v, @strong w => @default-return true, move || false);
}
#[test]
fn test_clone_macro_double_rename() {
let v = Rc::new(1);
let w = Rc::new(2);
let closure = clone!(@weak v as x, @weak w => @default-panic, move |_x| {
println!("v: {}, w: {}", x, w);
});
closure(0i8);
let _ =
clone!(@weak v as x, @weak w => @default-panic, move || println!("v: {}, w: {}", x, w));
let closure = clone!(@weak v, @weak w as x => @default-panic, move |_x| {
println!("v: {}, w: {}", v, x);
});
closure(0i8);
let _ =
clone!(@weak v, @weak w as x => @default-panic, move || println!("v: {}, w: {}", v, x));
let closure = clone!(@strong v as x, @strong w => @default-panic, move |_x| {
println!("v: {}, w: {}", x, w);
});
closure(0i8);
let _ = clone!(@strong v as x, @strong w => @default-panic, move || println!("v: {}, w: {}", x, w));
let closure = clone!(@strong v, @strong w as x => @default-panic, move |_x| {
println!("v: {}, w: {}", v, x);
});
closure(0i8);
let _ = clone!(@strong v, @strong w as x => @default-panic, move || println!("v: {}, w: {}", v, x));
let closure = clone!(@weak v as x, @weak w => move |_x| {
println!("v: {}, w: {}", x, w);
});
closure(0i8);
let _ = clone!(@weak v as x, @weak w => move || println!("v: {}, w: {}", x, w));
let closure = clone!(@weak v, @weak w as x => move |_x| {
println!("v: {}, w: {}", v, x);
});
closure(0i8);
let _ = clone!(@weak v, @weak w as x => move || println!("v: {}, w: {}", v, x));
let closure = clone!(@strong v as x, @strong w => move |_x| {
println!("v: {}, w: {}", x, w);
});
closure(0i8);
let _ = clone!(@strong v as x, @strong w => move || println!("v: {}, w: {}", x, w));
let closure = clone!(@strong v, @strong w as x => move |_x| {
println!("v: {}, w: {}", v, x);
});
closure(0i8);
let _ = clone!(@strong v, @strong w as x => move || println!("v: {}, w: {}", v, x));
let closure = clone!(@weak v as _x, @weak w => @default-return true, move |_| {
false
});
closure(0u8);
let _ = clone!(@weak v as _x, @weak w => @default-return true, move || false);
let closure = clone!(@weak v, @weak w as _x => @default-return true, move |_| {
false
});
closure(0i8);
let _ = clone!(@weak v, @weak w as _x => @default-return true, move || false);
let closure =
clone!(@strong v as _x, @strong w => @default-return true, move |_| { false });
closure(0i8);
let _ = clone!(@strong v as _x, @strong w => @default-return true, move || false);
let closure =
clone!(@strong v, @strong w as _x => @default-return true, move |_| { false });
closure(0i8);
let _ = clone!(@strong v, @strong w as _x => @default-return true, move || false);
}
#[test]
fn test_clone_macro_typed_args() {
let v = Rc::new(1);
let w = Rc::new(2);
let closure = clone!(@weak v as x, @weak w => @default-panic, move |_x: i8| {
println!("v: {}, w: {}", x, w);
});
let closure = clone!(@weak v, @weak w as x => @default-panic, move |_x: i8| {
println!("v: {}, w: {}", v, x);
});
let closure = clone!(@strong v as x, @strong w => @default-panic, move |_x: i8| {
println!("v: {}, w: {}", x, w);
});
let closure = clone!(@strong v, @strong w as x => @default-panic, move |_x: i8| {
println!("v: {}, w: {}", v, x);
});
let closure = clone!(@weak v as x, @weak w => move |_x: i8| {
println!("v: {}, w: {}", x, w);
});
let closure = clone!(@weak v, @weak w as x => move |_x: i8| {
println!("v: {}, w: {}", v, x);
});
let closure = clone!(@weak v, @weak w as x => move |_: i8, _| {
println!("v: {}, w: {}", v, x);
});
closure(0, 'a');
}
}