use stepflow_base::{ObjectStoreContent, ObjectStoreFiltered, generate_id_type, IdError};
use stepflow_data::{StateData, StateDataFiltered, value::Value, var::{Var, VarId}};
use stepflow_step::{Step};
use crate::ActionError;
mod action_string_template;
pub use action_string_template::StringTemplateAction;
mod action_htmlform;
pub use action_htmlform::{HtmlFormAction, HtmlFormConfig};
mod action_set_data;
pub use action_set_data::SetDataAction;
generate_id_type!(ActionId);
#[derive(Debug, Clone)]
pub enum ActionResult {
StartWith(Box<dyn Value>),
Finished(StateData),
CannotFulfill,
}
impl PartialEq for ActionResult {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(ActionResult::StartWith(val), ActionResult::StartWith(val_other)) => {
val == val_other
},
(ActionResult::Finished(data), ActionResult::Finished(data_other)) => {
data == data_other
},
(ActionResult::CannotFulfill, ActionResult::CannotFulfill) => {
true
},
(ActionResult::StartWith(_), _) |
(ActionResult::Finished(_), _) |
(ActionResult::CannotFulfill, _) => {
false
},
}
}
}
pub trait Action: std::fmt::Debug + stepflow_base::as_any::AsAny {
fn id(&self) -> &ActionId;
fn start(&mut self, step: &Step, step_name: Option<&str>, step_data: &StateDataFiltered, vars: &ObjectStoreFiltered<Box<dyn Var + Send + Sync>, VarId>)
-> Result<ActionResult, ActionError>;
}
impl dyn Action + Send + Sync {
pub fn downcast<T>(&self) -> Option<&T>
where T: Action + std::any::Any
{
self.as_any().downcast_ref::<T>()
}
pub fn is<T>(&self) -> bool
where T: Action + std::any::Any
{
self.as_any().is::<T>()
}
}
impl ObjectStoreContent for Box<dyn Action + Sync + Send> {
type IdType = ActionId;
fn new_id(id_val: u16) -> Self::IdType {
ActionId::new(id_val)
}
fn id(&self) -> &Self::IdType {
self.as_ref().id()
}
}
#[cfg(test)]
pub fn test_action_setup<'a>() -> (Step, StateData, stepflow_base::ObjectStore<Box<dyn Var + Send + Sync>, VarId>, VarId, Box<dyn stepflow_data::value::Value>) {
let mut var_store: stepflow_base::ObjectStore<Box<dyn Var + Send + Sync>, VarId> = stepflow_base::ObjectStore::new();
let var_id = var_store.insert_new(|id| Ok(stepflow_data::var::StringVar::new(id).boxed())).unwrap();
let var = var_store.get(&var_id).unwrap();
let state_val = stepflow_data::value::StringValue::try_new("hi").unwrap().boxed();
let mut state_data = StateData::new();
state_data.insert(var, state_val.clone()).unwrap();
let step = Step::new(stepflow_step::StepId::new(2), None, vec![]);
(step, state_data, var_store, var_id, state_val)
}
#[cfg(test)]
mod tests {
use stepflow_test_util::test_id;
use stepflow_data::{StateData, value::TrueValue};
use super::{ActionId, HtmlFormAction, SetDataAction, ActionResult};
#[test]
fn eq() {
let result_start = ActionResult::StartWith(TrueValue::new().boxed());
let result_finish = ActionResult::Finished(StateData::new());
let result_cannot = ActionResult::CannotFulfill;
assert_eq!(result_start, result_start);
assert_ne!(result_start, result_finish);
assert_ne!(result_start, result_cannot);
assert_eq!(result_finish, result_finish);
assert_ne!(result_finish, result_cannot);
}
#[test]
fn downcast() {
let action = HtmlFormAction::new(test_id!(ActionId), Default::default()).boxed();
assert!(action.is::<HtmlFormAction>());
assert!(!action.is::<SetDataAction>());
}
}