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 {
($($(@ $strength:ident)? self),+ => $($_:tt)* ) => (
compile_error!("Can't use `self` as variable name. Try storing it in a temporary variable or rename it.");
);
($($(@ $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-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-panic, move | $($pattern:pat),* | $body:block ) => (
{
$( $crate::to_type_before!($(@ $strength)? $($variables).+ $(as $rename)?); )*
move |$($pattern),*| {
$( $crate::to_type_after!($(as $rename)? @default-panic, $(@ $strength)? $($variables).+);)*
$body
}
}
);
($($(@ $strength:ident)? $($variables:ident).+ $(as $rename:ident)?),+ => $(@default-return $return_value:expr,)? move | $($pattern:pat),* | $body:block ) => (
{
$( $crate::to_type_before!($(@ $strength)? $($variables).+ $(as $rename)?); )*
move |$($pattern),*| {
let return_value = || $crate::to_return_value!($($return_value)?);
$( $crate::to_type_after!($(as $rename)? $(@ $strength)? $($variables).+, return_value);)*
$body
}
}
);
($($(@ $strength:ident)? $variables:expr),+ => $($_:tt)* ) => (
compile_error!("Variables need to be valid identifiers, e.g. field accesses are not allowed");
);
}