use std::rc::{Rc, Weak};
use crate::actor::event_bus::EventBus;
use crate::actor::registry::DebugRegistry;
use crate::actor::{Addr, ManagedActor, UiThreadToken};
use crate::scope::{Reducer, Scope};
pub struct Push<R: Reducer> {
scope: Weak<Scope>,
reducer: std::marker::PhantomData<R>,
}
impl<R: Reducer> Clone for Push<R> {
fn clone(&self) -> Self {
Self {
scope: self.scope.clone(),
reducer: std::marker::PhantomData,
}
}
}
impl<R: Reducer> std::fmt::Debug for Push<R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Push")
.field("reducer", &std::any::type_name::<R>())
.field("scope_alive", &(self.scope.strong_count() > 0))
.finish()
}
}
impl<R: Reducer> Push<R> {
fn new(scope: &Rc<Scope>) -> Self {
Self {
scope: Rc::downgrade(scope),
reducer: std::marker::PhantomData,
}
}
pub fn send(&self, update: R::Update) {
if let Some(scope) = self.scope.upgrade() {
crate::trace::mark(|| crate::trace::Point::Push {
reducer: crate::actor::short_type_name::<R>(),
});
scope.push::<R>(update);
}
}
}
#[derive(Clone, Default)]
pub struct Dispatch {
at: Option<(Weak<Scope>, usize)>,
}
impl Dispatch {
pub fn owning<R: 'static>(scope: &Rc<Scope>) -> Self {
Self::in_section(scope, scope.section_of::<R>())
}
pub(crate) fn in_section(scope: &Rc<Scope>, section: usize) -> Self {
Self {
at: Some((Rc::downgrade(scope), section)),
}
}
pub fn emit<M: 'static>(&self, action: M) {
let found = self
.at
.as_ref()
.and_then(|(scope, section)| Some((scope.upgrade()?, *section)))
.and_then(|(scope, section)| scope.answerer::<M>(section));
match found {
Some(answer) => {
let _action = crate::trace::enter(|| crate::trace::Point::Action {
message: crate::actor::short_type_name::<M>(),
});
answer(action)
}
None => tracing::warn!(
action = crate::actor::short_type_name::<M>(),
"the feature this dispatcher belongs to does not answer this action; dropped"
),
}
}
}
pub trait Exported {
fn mark(scope: &Scope);
fn unclaimed(scope: &Scope) -> Option<&'static str>;
}
fn missing<R: Reducer>(scope: &Scope) -> Option<&'static str> {
(!scope.claims::<R>()).then(|| std::any::type_name::<R>())
}
impl Exported for () {
fn mark(_scope: &Scope) {}
fn unclaimed(_scope: &Scope) -> Option<&'static str> {
None
}
}
macro_rules! exported {
($($reducer:ident),+) => {
impl<$($reducer: Reducer),+> Exported for ($($reducer,)+) {
fn mark(scope: &Scope) {
$(scope.note_export::<$reducer>();)+
}
fn unclaimed(scope: &Scope) -> Option<&'static str> {
None$(.or_else(|| missing::<$reducer>(scope)))+
}
}
};
}
exported!(A);
exported!(A, B);
exported!(A, B, C);
exported!(A, B, C, D);
exported!(A, B, C, D, E);
exported!(A, B, C, D, E, F);
exported!(A, B, C, D, E, F, G);
exported!(A, B, C, D, E, F, G, H);
exported!(A, B, C, D, E, F, G, H, I);
exported!(A, B, C, D, E, F, G, H, I, J);
exported!(A, B, C, D, E, F, G, H, I, J, K);
exported!(A, B, C, D, E, F, G, H, I, J, K, L);
pub trait Serves: Sized + 'static {
fn serve(addr: &Addr<Self>, scope: &Rc<Scope>);
}
pub struct Claim<'a, R: Reducer> {
scope: &'a Rc<Scope>,
bus: &'a Rc<EventBus>,
token: &'a UiThreadToken,
debug: &'a Rc<DebugRegistry>,
reducer: std::marker::PhantomData<fn() -> R>,
}
impl<'a, R: Reducer> Claim<'a, R> {
pub fn new(
scope: &'a Rc<Scope>,
bus: &'a Rc<EventBus>,
token: &'a UiThreadToken,
debug: &'a Rc<DebugRegistry>,
) -> Self {
scope.note_reducer_owner::<R>();
Self {
scope,
bus,
token,
debug,
reducer: std::marker::PhantomData,
}
}
pub fn seed(self, state: R) -> Self {
if self.scope.peek::<R>().is_none() {
self.scope.seed::<R>(state);
}
self
}
pub fn driven_by<A, F>(self, build: F) -> (Bound<R>, Addr<A>)
where
F: FnOnce(Push<R>) -> A,
A: ManagedActor + Serves + std::fmt::Debug + 'static,
{
let actor = Addr::new_managed_scoped(build(Push::new(self.scope)), self.token.clone());
actor.live_in(self.scope, self.bus);
A::serve(&actor, self.scope);
self.debug.register_owned(
&actor,
crate::actor::registry::Owner {
scope: Some(self.scope.key()),
feature: self.scope.current_feature(),
drives: Some(crate::actor::short_type_name::<R>()),
},
);
self.scope.own(Unregister {
id: actor.id(),
registry: self.debug.clone(),
});
self.scope.own(actor.clone());
(self.bound(), actor)
}
pub fn plain(self) -> Bound<R> {
self.bound()
}
fn bound(self) -> Bound<R> {
Bound {
push: Push::new(self.scope),
dispatch: Dispatch::in_section(self.scope, self.scope.current_section()),
}
}
}
struct Unregister {
id: usize,
registry: Rc<DebugRegistry>,
}
impl crate::scope::Teardown for Unregister {
fn teardown(self) {
self.registry.unregister(self.id);
}
}
pub struct Bound<R: Reducer> {
push: Push<R>,
dispatch: Dispatch,
}
impl<R: Reducer> Clone for Bound<R> {
fn clone(&self) -> Self {
Self {
push: self.push.clone(),
dispatch: self.dispatch.clone(),
}
}
}
impl<R: Reducer> Bound<R> {
pub fn push(&self, update: R::Update) {
self.push.send(update);
}
pub fn emit<M: 'static>(&self, action: M) {
self.dispatch.emit(action);
}
pub fn port(&self) -> Push<R> {
self.push.clone()
}
}