use std::any::TypeId;
use std::fmt::{Debug, Error, Formatter};
use std::hash::{Hash, Hasher};
use std::sync::{
atomic::{AtomicPtr, AtomicUsize, Ordering},
Arc,
};
use colored::Colorize;
use log::debug;
use futures::channel::mpsc::{TrySendError, UnboundedSender};
use crate::component::{Component, ComponentTask};
pub struct Scope<C: Component> {
name: &'static str,
muted: Arc<AtomicUsize>,
channel: UnboundedSender<C::Message>,
}
impl<C: Component> Scope<C> {
pub(crate) fn new(name: &'static str, channel: UnboundedSender<C::Message>) -> Self {
Scope {
name,
muted: Default::default(),
channel,
}
}
}
impl<C: Component> Clone for Scope<C> {
fn clone(&self) -> Self {
Scope {
name: self.name,
muted: self.muted.clone(),
channel: self.channel.clone(),
}
}
}
impl<C: Component> Eq for Scope<C> {}
impl<C: Component> PartialEq for Scope<C> {
fn eq(&self, other: &Self) -> bool {
self.channel.same_receiver(&other.channel)
}
}
impl<C: Component> Hash for Scope<C> {
fn hash<H: Hasher>(&self, h: &mut H) {
self.channel.hash_receiver(h)
}
}
impl<C: Component> Debug for Scope<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
write!(f, "Scope[{}]:({:?})", self.name, self.channel)
}
}
impl<C: 'static + Component> Scope<C> {
pub(crate) fn inherit<Child: Component>(
&self,
name: &'static str,
channel: UnboundedSender<Child::Message>,
) -> Scope<Child> {
Scope {
name,
muted: self.muted.clone(),
channel,
}
}
pub(crate) fn is_muted(&self) -> bool {
self.muted.load(Ordering::SeqCst) > 0
}
pub(crate) fn mute(&self) {
self.muted.fetch_add(1, Ordering::SeqCst);
}
pub(crate) fn unmute(&self) {
self.muted.fetch_sub(1, Ordering::SeqCst);
}
pub(crate) fn current_parent() -> Self {
ComponentTask::<_, C>::current_parent_scope()
}
#[inline(always)]
fn log(&self, message: &C::Message) {
debug!(
"{} {}: {}",
format!(
"Scope::send_message{}",
if self.is_muted() { " [muted]" } else { "" }
)
.green(),
self.name.magenta().bold(),
format!("{:?}", message).bright_white().bold()
);
}
#[doc(hidden)]
pub fn send_message(&self, message: C::Message) {
self.log(&message);
if !self.is_muted() {
self.channel
.unbounded_send(message)
.expect("channel has gone unexpectedly out of scope!");
}
}
pub fn try_send(&self, message: C::Message) -> Result<(), TrySendError<C::Message>> {
self.log(&message);
self.channel.unbounded_send(message)
}
pub fn name(&self) -> &'static str {
&self.name
}
}
pub(crate) struct AnyScope {
type_id: TypeId,
ptr: AtomicPtr<()>,
drop: Box<dyn Fn(&mut AtomicPtr<()>) + Send>,
}
impl<C: 'static + Component> From<Scope<C>> for AnyScope {
fn from(scope: Scope<C>) -> Self {
let ptr = AtomicPtr::new(Box::into_raw(Box::new(scope)) as *mut ());
let drop = |ptr: &mut AtomicPtr<()>| {
let ptr = ptr.swap(std::ptr::null_mut(), Ordering::SeqCst);
if !ptr.is_null() {
#[allow(unsafe_code)]
let scope = unsafe { Box::from_raw(ptr as *mut Scope<C>) };
std::mem::drop(scope)
}
};
AnyScope {
type_id: TypeId::of::<C::Properties>(),
ptr,
drop: Box::new(drop),
}
}
}
impl Drop for AnyScope {
fn drop(&mut self) {
(self.drop)(&mut self.ptr)
}
}
impl AnyScope {
pub(crate) fn try_get<C: 'static + Component>(&self) -> Option<&'static Scope<C>> {
if TypeId::of::<C::Properties>() == self.type_id {
#[allow(unsafe_code)]
unsafe {
(self.ptr.load(Ordering::Relaxed) as *const Scope<C>).as_ref()
}
} else {
None
}
}
}