async_step_machine 0.1.0

A Simple Step Machine to improve code maintenance to execute async steps.
Documentation
use std::cmp::PartialEq;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::cell::RefCell;
use std::rc::Rc;
use std::future::Future;
use std::pin::Pin;

pub type StepLabel = i32;
pub type StepError = i32;

#[derive(Eq, PartialEq, Hash)]
pub enum StepMachineLabel {
    Done,
    StepLabel(StepLabel)
}

#[derive(Eq, PartialEq)]
pub enum StepMachineError {
    InternalError,
    InexistentStep,
    StepError(StepError)
}

pub type Step<T,V> = dyn Fn(Option<Rc<RefCell<T>>>, Option<Rc<RefCell<Vec<Arc<RwLock<V>>>>>>) -> Pin<Box<dyn Future<Output = Result<Option<StepMachineLabel>,StepMachineError>>>>;

pub type ErrorHandler<T,V> = dyn Fn(StepLabel, StepMachineError, Option<Rc<RefCell<T>>>, Option<Rc<RefCell<Vec<Arc<RwLock<V>>>>>>) -> Pin<Box<dyn Future<Output = StepMachineError>>>;

pub struct StepMachine<T,V>
{
    sync_handler: Option<Rc<RefCell<T>>>,
    async_handlers: Option<Rc<RefCell<Vec<Arc<RwLock<V>>>>>>,
    steps: HashMap<StepMachineLabel,Box<Step<T,V>>>,
    error_handler: Option<Box<ErrorHandler<T,V>>>
}

impl<T,V> StepMachine<T,V>
{
    pub fn new(sync_handler:Option<Rc<RefCell<T>>>, async_handlers:Option<Rc<RefCell<Vec<Arc<RwLock<V>>>>>>, steps: Vec<(StepMachineLabel,Box<Step<T,V>>)>, error_handler: Option<Box<ErrorHandler<T,V>>>) -> Self {
        Self {
            sync_handler: sync_handler,
            async_handlers: async_handlers,
            steps: steps.into_iter().collect(),
            error_handler: error_handler
        }
    }

    fn clone_handlers(&self) -> (Option<Rc<RefCell<T>>>,Option<Rc<RefCell<Vec<Arc<RwLock<V>>>>>>) {
        (
            self.sync_handler.as_ref().map(|h| h.clone()),
            self.async_handlers.as_ref().map(|h| h.clone()),
        )
    }

    pub async fn run(&mut self, beginning:StepMachineLabel) -> Result<(),StepMachineError> {

        let mut last_step = beginning;

        while let Some(step) = self.steps.get(&last_step) {

            let (sync_handler,async_handlers) = self.clone_handlers();

            match step(sync_handler, async_handlers).await {

                Ok(Some(StepMachineLabel::Done)) => return Ok(()),

                Ok(Some(next_step)) => {
                    last_step = next_step;
                }

                Ok(None) => return Ok(()),
                
                Err(error_code) => {
                    if let Some(err_handler) = &self.error_handler {
                        if let StepMachineLabel::StepLabel(last_step_label) = last_step {
                            let (sync_handler,async_handlers) = self.clone_handlers();
                            return Err(err_handler(last_step_label,error_code,sync_handler,async_handlers).await);
                        } else {
                            return Err(StepMachineError::InternalError);
                        }
                    }
                    return Err(error_code);
                }
            }
        }
    
        return Err(StepMachineError::InexistentStep);

    }
}