use crate::{
observer::{Flow, Observer, Termination},
utils::{
mutable::{Mutable, MutableExt, MutableHelper},
on_panic::on_panic,
pending_events::{EventBatch, PendingEvents},
types::{Shared, WeakShared},
},
};
use educe::Educe;
#[derive(Educe)]
#[educe(Debug, Clone)]
pub struct SerializedDelivery<T, E, OR, R>(Shared<Mutable<State<T, E, OR, R>>>);
#[derive(Educe)]
#[educe(Debug, Clone)]
pub struct WeakSerializedDelivery<T, E, OR, R>(WeakShared<Mutable<State<T, E, OR, R>>>);
#[derive(Educe)]
#[educe(Debug)]
enum State<T, E, OR, R> {
Idle { observer: OR, resources: R },
Delivering {
pending: PendingEvents<T, E>,
resources: R,
},
Stopped,
}
enum EnqueueAction<T, E, OR> {
Start { observer: OR, first_next: Option<T> },
Accepted,
Rejected(EventBatch<T, E>),
}
enum Step<T, E, OR, R> {
Next(OR, T),
Terminate {
observer: OR,
termination: Termination<E>,
resources: R,
},
Parked,
Stopped(OR),
}
#[derive(Educe)]
#[educe(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DeliveryStopped;
pub struct DropUndecided;
pub struct DropDecided<T>(Option<T>);
#[derive(Educe)]
#[educe(Debug)]
pub struct UpdateOutcome<T, E, R = (), DO = DropUndecided, const EVENTS_DECIDED: bool = false> {
events: Option<EventBatch<T, E>>,
drop_outside: DO,
result: R,
}
impl<T, E, R> UpdateOutcome<T, E, R> {
pub fn new(result: R) -> Self {
Self {
events: None,
drop_outside: DropUndecided,
result,
}
}
}
impl<T, E> UpdateOutcome<T, E> {
pub fn empty() -> Self {
Self::new(())
}
}
impl<T, E, R, DO, const EVENTS_DECIDED: bool> UpdateOutcome<T, E, R, DO, EVENTS_DECIDED> {
pub(crate) fn into_parts(self) -> (Option<EventBatch<T, E>>, DO, R) {
(self.events, self.drop_outside, self.result)
}
}
impl<T, E, R, const EVENTS_DECIDED: bool> UpdateOutcome<T, E, R, DropUndecided, EVENTS_DECIDED> {
pub fn with_drop_outside<DO>(
self,
drop_outside: DO,
) -> UpdateOutcome<T, E, R, DropDecided<DO>, EVENTS_DECIDED> {
UpdateOutcome {
events: self.events,
drop_outside: DropDecided(Some(drop_outside)),
result: self.result,
}
}
pub fn without_drop_outside<DO>(
self,
) -> UpdateOutcome<T, E, R, DropDecided<DO>, EVENTS_DECIDED> {
UpdateOutcome {
events: self.events,
drop_outside: DropDecided(None),
result: self.result,
}
}
}
impl<T, E, R, DO> UpdateOutcome<T, E, R, DO, false> {
pub fn with_next_event(self, next: T) -> UpdateOutcome<T, E, R, DO, true> {
self.with_events(EventBatch::Next(next))
}
pub fn with_termination_event(
self,
termination: Termination<E>,
) -> UpdateOutcome<T, E, R, DO, true> {
self.with_events(EventBatch::Termination(termination))
}
pub fn with_next_and_termination_events(
self,
next: T,
termination: Termination<E>,
) -> UpdateOutcome<T, E, R, DO, true> {
self.with_events(EventBatch::NextAndTermination(next, termination))
}
pub fn with_events(self, events: EventBatch<T, E>) -> UpdateOutcome<T, E, R, DO, true> {
UpdateOutcome {
events: Some(events),
drop_outside: self.drop_outside,
result: self.result,
}
}
pub fn without_events(self) -> UpdateOutcome<T, E, R, DO, true> {
UpdateOutcome {
events: None,
drop_outside: self.drop_outside,
result: self.result,
}
}
}
impl<T, E, OR, R> SerializedDelivery<T, E, OR, R> {
pub fn idle(observer: OR, resources: R) -> Self {
Self(Shared::new(Mutable::new(State::Idle {
observer,
resources,
})))
}
pub fn stop(&self) {
let _deferred_drop = self.0.replace_value(State::Stopped);
}
pub fn downgrade(&self) -> WeakSerializedDelivery<T, E, OR, R> {
WeakSerializedDelivery(Shared::downgrade(&self.0))
}
}
impl<T, E, OR, R> SerializedDelivery<T, E, OR, R>
where
OR: Observer<T, E>,
{
pub fn send(&self, events: EventBatch<T, E>) -> Flow {
let ends_stream = events.ends_stream();
let action = self.0.with_mut(|state| state.enqueue_batch(events));
let flow = self.perform(action);
if ends_stream { Flow::Stop } else { flow }
}
pub fn update<Out, DO, const EVENTS_DECIDED: bool>(
&self,
update: impl FnOnce(&mut R) -> UpdateOutcome<T, E, Out, DO, EVENTS_DECIDED>,
) -> Result<Out, DeliveryStopped> {
self.update_with_flow(update).map(|(result, _)| result)
}
pub fn update_with_flow<Out, DO, const EVENTS_DECIDED: bool>(
&self,
update: impl FnOnce(&mut R) -> UpdateOutcome<T, E, Out, DO, EVENTS_DECIDED>,
) -> Result<(Out, Flow), DeliveryStopped> {
let mut update = Some(update);
let (action, drop_outside, result) = self
.0
.with_mut(|state| {
let resources = state.resources_mut()?;
let update = update.take().expect("the update runs at most once");
let UpdateOutcome {
events,
drop_outside,
result,
} = update(resources);
let action =
events.map(|events| (events.ends_stream(), state.enqueue_batch(events)));
Some((action, drop_outside, result))
})
.ok_or(DeliveryStopped)?;
let flow = match action {
Some((true, action)) => {
let _ = self.perform(action);
Flow::Stop
}
Some((false, action)) => self.perform(action),
None => Flow::Continue,
};
drop(drop_outside); Ok((result, flow))
}
fn perform(&self, action: EnqueueAction<T, E, OR>) -> Flow {
match action {
EnqueueAction::Start {
observer,
first_next,
} => self.deliver(observer, first_next),
EnqueueAction::Accepted => Flow::Continue,
EnqueueAction::Rejected(events) => {
drop(events); Flow::Stop
}
}
}
fn deliver(&self, mut observer: OR, first_next: Option<T>) -> Flow {
if let Some(value) = first_next {
let guard = on_panic(|| self.stop());
let flow = observer.on_next(value);
drop(guard);
if flow.is_stop() {
return self.stop_with(observer);
}
}
loop {
match self.0.with_mut(|state| state.next_step(observer)) {
Step::Next(next_observer, value) => {
observer = next_observer;
let guard = on_panic(|| self.stop());
let flow = observer.on_next(value);
drop(guard);
if flow.is_stop() {
return self.stop_with(observer);
}
}
Step::Terminate {
observer,
termination,
resources,
} => {
let guard = on_panic(|| self.stop());
observer.on_termination(termination);
drop(guard);
drop(resources);
return Flow::Stop;
}
Step::Parked => return Flow::Continue,
Step::Stopped(observer) => {
drop(observer); return Flow::Stop;
}
}
}
}
fn stop_with(&self, observer: OR) -> Flow {
self.stop();
drop(observer); Flow::Stop
}
}
impl<T, E, OR, R> WeakSerializedDelivery<T, E, OR, R> {
pub fn upgrade(&self) -> Option<SerializedDelivery<T, E, OR, R>> {
self.0.upgrade().map(SerializedDelivery)
}
}
impl<T, E, OR, R> State<T, E, OR, R> {
fn resources_mut(&mut self) -> Option<&mut R> {
match self {
Self::Idle { resources, .. } | Self::Delivering { resources, .. } => Some(resources),
Self::Stopped => None,
}
}
fn enqueue_batch(&mut self, events: EventBatch<T, E>) -> EnqueueAction<T, E, OR> {
match self {
Self::Delivering { pending, .. } => match pending.push_batch(events) {
Some(rejected) => EnqueueAction::Rejected(rejected),
None => EnqueueAction::Accepted,
},
Self::Stopped => EnqueueAction::Rejected(events),
Self::Idle { .. } => {
let (first_next, pending) = PendingEvents::from_batch(events);
if first_next.is_none() && pending.is_empty() {
return EnqueueAction::Accepted;
}
let Self::Idle {
observer,
resources,
} = std::mem::replace(self, Self::Stopped)
else {
unreachable!()
};
*self = Self::Delivering { pending, resources };
EnqueueAction::Start {
observer,
first_next,
}
}
}
}
fn next_step(&mut self, observer: OR) -> Step<T, E, OR, R> {
match self {
Self::Delivering { pending, .. } => {
if let Some(value) = pending.pop_next() {
return Step::Next(observer, value);
}
}
Self::Stopped => return Step::Stopped(observer),
Self::Idle { .. } => unreachable!("a delivery loop only runs in the delivering state"),
}
let Self::Delivering {
mut pending,
resources,
} = std::mem::replace(self, Self::Stopped)
else {
unreachable!()
};
match pending.take_termination() {
Some(termination) => {
drop(pending);
Step::Terminate {
observer,
termination,
resources,
}
}
None => {
*self = Self::Idle {
observer,
resources,
};
Step::Parked
}
}
}
}