#![doc = include_str!("../README.md")]
#![no_std]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
pub mod adapter;
pub mod error;
#[cfg(feature = "alloc")]
pub mod output;
#[cfg(feature = "tokio")]
use tokio::sync::mpsc::{channel, Receiver, Sender};
use crate::{
adapter::{Adapter, Feed, Placeholder},
error::Result,
};
use core::future::Future;
use edfsm::{Drain, Fsm, Init, Input, Terminating};
pub type Event<M> = <M as Fsm>::E;
pub type Command<M> = <M as Fsm>::C;
pub type In<M> = Input<<M as Fsm>::C, <M as Fsm>::E>;
pub type Out<M> = <<M as Fsm>::SE as Drain>::Item;
pub type Effects<M> = <M as Fsm>::SE;
pub type State<M> = <M as Fsm>::S;
pub trait Machine<M>
where
M: Fsm,
Effects<M>: Drain,
{
fn input(&self) -> Sender<In<M>>;
fn with_output(self, output: impl Adapter<Item = Out<M>> + 'static) -> impl Machine<M>;
fn merge_output(self, output: impl Adapter<Item = Out<M>> + 'static) -> impl Machine<M>
where
Out<M>: Clone + Send;
fn with_event_log(
self,
log: impl Adapter<Item = Event<M>> + Feed<Item = Event<M>> + 'static,
) -> impl Machine<M>;
fn merge_event_log(self, output: impl Adapter<Item = Event<M>> + 'static) -> impl Machine<M>;
fn task(self) -> impl Future<Output = Result<()>> + Send + 'static
where
Self: Sized,
Out<M>: Send,
Event<M>: Send + Terminating,
Effects<M>: Init<State<M>> + Send,
Command<M>: Send,
State<M>: Default + Send;
}
struct Template<M, N, O, P>
where
M: Fsm,
{
sender: Option<Sender<In<M>>>,
receiver: Receiver<In<M>>,
effects: Effects<M>,
log: N,
output: O,
events: P,
}
impl<M, N, O, P> Machine<M> for Template<M, N, O, P>
where
M: Fsm + 'static,
Effects<M>: Drain,
N: Adapter<Item = Event<M>> + Feed<Item = Event<M>> + 'static,
O: Adapter<Item = Out<M>> + 'static,
P: Adapter<Item = Event<M>> + 'static,
Event<M>: Clone + Send,
{
fn input(&self) -> Sender<In<M>> {
self.sender.as_ref().unwrap().clone()
}
fn with_output(self, output: impl Adapter<Item = Out<M>> + 'static) -> impl Machine<M> {
Template {
sender: self.sender,
receiver: self.receiver,
effects: self.effects,
log: self.log,
output,
events: self.events,
}
}
fn merge_output(self, output: impl Adapter<Item = Out<M>> + 'static) -> impl Machine<M>
where
Out<M>: Clone + Send,
{
Template {
sender: self.sender,
receiver: self.receiver,
effects: self.effects,
log: self.log,
output: self.output.merge(output),
events: self.events,
}
}
fn with_event_log(
self,
log: impl Adapter<Item = Event<M>> + Feed<Item = Event<M>> + 'static,
) -> impl Machine<M> {
Template {
sender: self.sender,
receiver: self.receiver,
effects: self.effects,
log,
output: self.output,
events: self.events,
}
}
fn merge_event_log(self, events: impl Adapter<Item = Event<M>> + 'static) -> impl Machine<M> {
Template {
sender: self.sender,
receiver: self.receiver,
effects: self.effects,
log: self.log,
output: self.output,
events: self.events.merge(events),
}
}
async fn task(mut self) -> Result<()>
where
Effects<M>: Init<State<M>>,
State<M>: Default,
Event<M>: Send + Terminating,
State<M>: Send,
{
self.sender = None;
let mut state: State<M> = Default::default();
let mut hydra = Hydrator::<M> { state: &mut state };
self.log.feed(&mut hydra).await?;
self.effects.init(&state);
for item in self.effects.drain_all() {
self.output.notify(item).await
}
while let Some(input) = self.receiver.recv().await {
let mut terminating = false;
if let Some(e) = M::step(&mut state, input, &mut self.effects) {
terminating = e.terminating();
self.log.clone_notify(&e).await;
self.events.notify(e).await;
}
for item in self.effects.drain_all() {
self.output.notify(item).await
}
if terminating {
break;
}
}
Ok(())
}
}
pub const DEFAULT_BUFFER: usize = 10;
pub fn machine<M>() -> impl Machine<M>
where
M: Fsm + 'static,
Effects<M>: Drain + Default,
Out<M>: Send + Clone,
Event<M>: Send + Sync + Clone,
{
machine_with_effects(Default::default(), DEFAULT_BUFFER)
}
pub fn machine_with_effects<M>(effects: Effects<M>, buffer: usize) -> impl Machine<M>
where
M: Fsm + 'static,
Effects<M>: Drain,
Out<M>: Send + Clone,
Event<M>: Send + Sync + Clone,
{
let (sender, receiver) = channel(buffer);
Template {
sender: Some(sender),
receiver,
effects,
log: Placeholder::default(),
output: Placeholder::default(),
events: Placeholder::default(),
}
}
struct Hydrator<'a, M>
where
M: Fsm,
{
state: &'a mut State<M>,
}
impl<M> Adapter for Hydrator<'_, M>
where
M: Fsm,
Event<M>: Send,
State<M>: Send,
{
type Item = Event<M>;
async fn notify(&mut self, a: Self::Item)
where
Self::Item: Send + 'static,
{
M::on_event(self.state, &a);
}
}