use crate::disposable::Disposable;
use crate::observer::{Flow, Observer, Termination, boxed_observer::BoxedObserver};
use crate::utils::id_generator::{Id, IdGenerator};
use crate::utils::mutable::{MutableBool, MutableBoolHelper};
use crate::utils::pending_events::EventBatch;
use crate::utils::serialized_delivery::{DeliveryStopped, SerializedDelivery, UpdateOutcome};
use crate::utils::types::{MaybeSend, Shared};
use educe::Educe;
#[derive(Educe)]
#[educe(Debug, Clone)]
pub struct SerializedMulticast<'or, T, E, R = ()>(Delivery<'or, T, E, R>);
type Delivery<'or, T, E, R> =
SerializedDelivery<Action<'or, T, E>, E, Subscribers<'or, T, E>, Resources<E, R>>;
#[derive(Educe)]
#[educe(Debug)]
struct Resources<E, R> {
termination: Option<Termination<E>>,
ids: IdGenerator,
host: R,
}
#[derive(Educe)]
#[educe(Debug)]
pub enum Admission<T, E> {
Join(Vec<T>),
Terminated(Vec<T>, Termination<E>),
}
#[derive(Educe)]
#[educe(Debug)]
enum Admitted<T, E> {
Added(Id),
Terminated(Vec<T>, Termination<E>),
}
impl<'or, T, E, R> SerializedMulticast<'or, T, E, R> {
pub fn idle(host: R) -> Self {
Self(SerializedDelivery::idle(
Subscribers {
entries: Vec::new(),
},
Resources {
termination: None,
ids: IdGenerator::default(),
host,
},
))
}
}
impl<'or, T, E, R> SerializedMulticast<'or, T, E, R>
where
T: Clone,
E: Clone,
{
pub fn terminated(&self) -> Option<Termination<E>> {
self.0
.update(|resources| UpdateOutcome::new(resources.termination.clone()))
.unwrap_or(None)
}
pub fn read<Out>(
&self,
read: impl FnOnce(&R, Option<&Termination<E>>) -> Out,
) -> Result<Out, DeliveryStopped> {
self.0.update(|resources| {
UpdateOutcome::new(read(&resources.host, resources.termination.as_ref()))
})
}
pub fn update<Out, DO, const EVENTS_DECIDED: bool>(
&self,
update: impl FnOnce(
&mut R,
Option<&Termination<E>>,
) -> UpdateOutcome<T, E, Out, DO, EVENTS_DECIDED>,
) -> Result<Out, DeliveryStopped> {
self.0.update(|resources| {
let (events, drop_outside, result) =
update(&mut resources.host, resources.termination.as_ref()).into_parts();
let outcome = UpdateOutcome::new(result).with_drop_outside(drop_outside);
match events {
Some(events) => {
outcome.with_events(into_actions(events, &mut resources.termination))
}
None => outcome.without_events(),
}
})
}
pub fn send(&self, events: EventBatch<T, E>) -> Flow {
self.update(|_, terminated| {
if terminated.is_some() {
return UpdateOutcome::new(Flow::Stop)
.with_drop_outside(events)
.without_events();
}
UpdateOutcome::new(Flow::Continue)
.without_drop_outside()
.with_events(events)
})
.unwrap_or(Flow::Stop)
}
pub fn subscribe(
self,
observer: impl Observer<T, E> + MaybeSend + 'or,
) -> Option<MulticastDisposal<'or, T, E, R>> {
self.subscribe_with(observer, |_, terminated| match terminated {
Some(termination) => Admission::Terminated(Vec::new(), termination.clone()),
None => Admission::Join(Vec::new()),
})
}
pub fn subscribe_with(
self,
observer: impl Observer<T, E> + MaybeSend + 'or,
admit: impl FnOnce(&mut R, Option<&Termination<E>>) -> Admission<T, E>,
) -> Option<MulticastDisposal<'or, T, E, R>> {
let disposed = Shared::new(MutableBool::new(false));
let mut observer = Some(observer);
let admitted = self.0.update(|resources| {
match admit(&mut resources.host, resources.termination.as_ref()) {
Admission::Terminated(values, termination) => {
UpdateOutcome::new(Admitted::Terminated(values, termination)).without_events()
}
Admission::Join(replay) => {
let id = resources.ids.next_id();
let entry = Entry {
id,
is_disposed: disposed.clone(),
observer: BoxedObserver::new(
observer.take().expect("the update runs at most once"),
),
};
UpdateOutcome::new(Admitted::Added(id))
.with_next_event(Action::Add { entry, replay })
}
}
});
match admitted {
Ok(Admitted::Added(id)) => Some(MulticastDisposal {
delivery: self.0,
disposed,
id,
}),
Ok(Admitted::Terminated(values, termination)) => {
let mut observer = observer.take().expect("the update left the observer here");
let mut flow = Flow::Continue;
for value in values {
flow = observer.on_next(value);
if flow.is_stop() {
break;
}
}
if flow.is_continue() {
observer.on_termination(termination);
}
None
}
Err(DeliveryStopped) => {
debug_assert!(false, "the multicast is dead because an observer panicked");
None
}
}
}
}
fn into_actions<'or, T, E>(
events: EventBatch<T, E>,
termination: &mut Option<Termination<E>>,
) -> EventBatch<Action<'or, T, E>, E>
where
E: Clone,
{
let mut record = |queued: Termination<E>| {
debug_assert!(
termination.is_none(),
"a host must not emit once the termination was queued"
);
*termination = Some(queued.clone());
Action::Terminate(queued)
};
match events {
EventBatch::Next(value) => EventBatch::Next(Action::Forward(value)),
EventBatch::Termination(termination) => EventBatch::Next(record(termination)),
EventBatch::NextAndTermination(value, termination) => {
EventBatch::NextBatch(vec![Action::Forward(value), record(termination)])
}
EventBatch::NextBatch(values) => {
EventBatch::NextBatch(values.into_iter().map(Action::Forward).collect())
}
EventBatch::NextBatchAndTermination(values, termination) => {
let mut actions: Vec<_> = values.into_iter().map(Action::Forward).collect();
actions.push(record(termination));
EventBatch::NextBatch(actions)
}
}
}
#[derive(Educe)]
#[educe(Debug)]
struct Entry<'or, T, E> {
id: Id,
is_disposed: Shared<MutableBool>,
observer: BoxedObserver<'or, T, E>,
}
#[derive(Educe)]
#[educe(Debug)]
enum Action<'or, T, E> {
Forward(T),
Add {
entry: Entry<'or, T, E>,
replay: Vec<T>,
},
Prune(Id),
Terminate(Termination<E>),
}
#[derive(Educe)]
#[educe(Debug)]
struct Subscribers<'or, T, E> {
entries: Vec<Entry<'or, T, E>>,
}
impl<'or, T, E> Observer<Action<'or, T, E>, E> for Subscribers<'or, T, E>
where
T: Clone,
E: Clone,
{
fn on_next(&mut self, action: Action<'or, T, E>) -> Flow {
match action {
Action::Forward(value) => self.forward(value),
Action::Add { entry, replay } => self.add(entry, replay),
Action::Prune(id) => self.prune(id),
Action::Terminate(termination) => self.terminate(termination),
}
Flow::Continue
}
fn on_termination(self, _: Termination<E>) {
debug_assert!(
false,
"the multicast's delivery never terminates: see the module documentation"
);
}
}
impl<'or, T, E> Subscribers<'or, T, E>
where
T: Clone,
E: Clone,
{
fn forward(&mut self, value: T) {
self.entries.retain_mut(|entry| {
if entry.is_disposed.read() {
return true;
}
entry.observer.on_next(value.clone()).is_continue()
});
}
fn add(&mut self, mut entry: Entry<'or, T, E>, replay: Vec<T>) {
for value in replay {
if entry.is_disposed.read() {
return; }
if entry.observer.on_next(value).is_stop() {
return; }
}
if entry.is_disposed.read() {
return; }
debug_assert!(
self.entries.last().is_none_or(|last| last.id < entry.id),
"the ids are handed out in subscription order"
);
self.entries.push(entry);
}
fn prune(&mut self, id: Id) {
if let Ok(index) = self.entries.binary_search_by_key(&id, |entry| entry.id) {
self.entries.remove(index); }
}
fn terminate(&mut self, termination: Termination<E>) {
for entry in std::mem::take(&mut self.entries) {
if entry.is_disposed.read() {
continue; }
entry.observer.on_termination(termination.clone());
}
}
}
pub struct MulticastDisposal<'or, T, E, R> {
delivery: Delivery<'or, T, E, R>,
disposed: Shared<MutableBool>,
id: Id,
}
impl<T, E, R> Disposable for MulticastDisposal<'_, T, E, R>
where
T: Clone,
E: Clone,
{
fn dispose(self) {
self.disposed.write(true);
let _ = self.delivery.send(EventBatch::Next(Action::Prune(self.id)));
}
}