use crate::free_lifetimes;
use core::any::{TypeId, Any, type_name};
use panicking::panicking;
pub trait State: 'static {
fn get_raw(&self, ty: TypeId) -> Option<&dyn Any>;
fn get_mut_raw(&mut self, ty: TypeId) -> Option<&mut dyn Any>;
}
#[cfg(feature="nightly")]
impl State for ! {
fn get_raw(&self, _ty: TypeId) -> Option<&dyn Any> { Some(self) }
fn get_mut_raw(&mut self, _ty: TypeId) -> Option<&mut dyn Any> { Some(self) }
}
impl State for () {
fn get_raw(&self, _ty: TypeId) -> Option<&dyn Any> { None }
fn get_mut_raw(&mut self, _ty: TypeId) -> Option<&mut dyn Any> { None }
}
pub trait Stop: Sized {
fn is_stopped(&self) -> bool;
fn stop(state: &mut dyn State);
fn drop(&mut self) {
if !self.is_stopped() && !panicking() {
panic!("{} requires explicit stop function call before dropping", type_name::<Self>());
}
}
}
pub trait SelfState: 'static { }
impl<T: SelfState> State for T {
fn get_raw(&self, ty: TypeId) -> Option<&dyn Any> {
if ty == TypeId::of::<T>() {
Some(self)
} else {
None
}
}
fn get_mut_raw(&mut self, ty: TypeId) -> Option<&mut dyn Any> {
if ty == TypeId::of::<T>() {
Some(self)
} else {
None
}
}
}
pub trait StateExt: State {
fn get<T: 'static>(&self) -> &T {
self.get_raw(TypeId::of::<T>())
.unwrap_or_else(|| panic!("{} required", type_name::<T>()))
.downcast_ref::<T>().expect("invalid cast")
}
fn get_mut<T: 'static>(&mut self) -> &mut T {
self.get_mut_raw(TypeId::of::<T>())
.unwrap_or_else(|| panic!("{} required", type_name::<T>()))
.downcast_mut::<T>().expect("invalid cast")
}
}
impl<T: State + ?Sized> StateExt for T { }
free_lifetimes! {
struct StateSum {
a: 'a ref dyn State,
b: 'b ref dyn State,
}
}
impl State for StateSum {
fn get_raw(&self, ty: TypeId) -> Option<&dyn Any> {
self.a().get_raw(ty).or_else(|| self.b().get_raw(ty))
}
fn get_mut_raw(&mut self, _ty: TypeId) -> Option<&mut dyn Any> {
unreachable!()
}
}
free_lifetimes! {
struct StateSumMut {
a: 'a mut dyn State,
b: 'b mut dyn State,
}
}
impl State for StateSumMut {
fn get_raw(&self, ty: TypeId) -> Option<&dyn Any> {
self.a().get_raw(ty).or_else(|| self.b().get_raw(ty))
}
fn get_mut_raw(&mut self, ty: TypeId) -> Option<&mut dyn Any> {
borrow_mut_either(
self,
|x| x.a_mut().get_mut_raw(ty),
|x| x.b_mut().get_mut_raw(ty)
)
}
}
fn borrow_mut_either<T: ?Sized, R: ?Sized>(
x: &mut T,
a: impl FnOnce(&mut T) -> Option<&mut R>,
b: impl FnOnce(&mut T) -> Option<&mut R>
) -> Option<&mut R> {
let r = if let Some (r) = a(x) {
r as *mut _
} else if let Some(r) = b(x) {
r as *mut _
} else {
return None;
};
Some(unsafe { &mut *r })
}
pub trait StateRef {
fn merge_and_then<T>(self, f: impl FnOnce(&dyn State) -> T, other: &dyn State) -> T;
}
impl<C: State> StateRef for &C {
fn merge_and_then<T>(self, f: impl FnOnce(&dyn State) -> T, other: &dyn State) -> T {
StateSumBuilder {
a: self,
b: other,
}.build_and_then(|x| f(x))
}
}
impl StateRef for &dyn State {
fn merge_and_then<T>(self, f: impl FnOnce(&dyn State) -> T, other: &dyn State) -> T {
StateSumBuilder {
a: self,
b: other,
}.build_and_then(|x| f(x))
}
}
pub trait StateRefMut {
fn merge_mut_and_then<T>(self, f: impl FnOnce(&mut dyn State) -> T, other: &mut dyn State) -> T;
}
impl<C: State> StateRefMut for &mut C {
fn merge_mut_and_then<T>(self, f: impl FnOnce(&mut dyn State) -> T, other: &mut dyn State) -> T {
StateSumMutBuilder {
a: self,
b: other,
}.build_and_then(|x| f(x))
}
}
impl StateRefMut for &mut dyn State {
fn merge_mut_and_then<T>(self, f: impl FnOnce(&mut dyn State) -> T, other: &mut dyn State) -> T {
StateSumMutBuilder {
a: self,
b: other,
}.build_and_then(|x| f(x))
}
}
#[macro_export]
macro_rules! impl_stop_and_drop {
(
$($token:tt)+
) => {
$crate::generics_parse! {
$crate::impl_stop_and_drop_impl {
}
$($token)+
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! impl_stop_and_drop_impl {
(
[$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
for $t:ty {
$($body:tt)*
}
) => {
impl $($g)* $crate::Stop for $t $($w)* {
$($body)*
}
impl $($g)* $crate::std_ops_Drop for $t $($w)* {
fn drop(&mut self) {
<$t as $crate::Stop>::drop(self);
}
}
};
(
[$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
$($token:tt)*
) => {
$crate::std_compile_error!($crate::indoc_indoc!("
invalid Stop trait implementation, allowed forms are
for $t:ty {
$($impl_stop_trait_body:tt)*
}
<$generics> for $t:ty $(where $where_clause)? {
$($impl_stop_trait_body:tt)*
}
"));
};
}