stepflow_action/
action.rs1use stepflow_base::{ObjectStoreContent, ObjectStoreFiltered, generate_id_type, IdError};
2use stepflow_data::{StateData, StateDataFiltered, value::Value, var::{Var, VarId}};
3use stepflow_step::{Step};
4use crate::ActionError;
5
6mod action_string_template;
7pub use action_string_template::StringTemplateAction;
8
9mod action_htmlform;
10pub use action_htmlform::{HtmlFormAction, HtmlFormConfig};
11
12mod action_set_data;
13pub use action_set_data::SetDataAction;
14
15generate_id_type!(ActionId);
16
17#[derive(Debug, Clone)]
19pub enum ActionResult {
20 StartWith(Box<dyn Value>),
33
34 Finished(StateData),
36
37 CannotFulfill,
40}
41
42impl PartialEq for ActionResult {
43 fn eq(&self, other: &Self) -> bool {
44 match (self, other) {
45 (ActionResult::StartWith(val), ActionResult::StartWith(val_other)) => {
46 val == val_other
47 },
48 (ActionResult::Finished(data), ActionResult::Finished(data_other)) => {
49 data == data_other
50 },
51 (ActionResult::CannotFulfill, ActionResult::CannotFulfill) => {
52 true
53 },
54 (ActionResult::StartWith(_), _) |
55 (ActionResult::Finished(_), _) |
56 (ActionResult::CannotFulfill, _) => {
57 false
58 },
59 }
60 }
61}
62
63pub trait Action: std::fmt::Debug + stepflow_base::as_any::AsAny {
65 fn id(&self) -> &ActionId;
67
68 fn start(&mut self, step: &Step, step_name: Option<&str>, step_data: &StateDataFiltered, vars: &ObjectStoreFiltered<Box<dyn Var + Send + Sync>, VarId>)
72 -> Result<ActionResult, ActionError>;
73}
74
75impl dyn Action + Send + Sync {
77 pub fn downcast<T>(&self) -> Option<&T>
78 where T: Action + std::any::Any
79 {
80 self.as_any().downcast_ref::<T>()
81 }
82 pub fn is<T>(&self) -> bool
83 where T: Action + std::any::Any
84 {
85 self.as_any().is::<T>()
86 }
87}
88
89impl ObjectStoreContent for Box<dyn Action + Sync + Send> {
90 type IdType = ActionId;
91
92 fn new_id(id_val: u16) -> Self::IdType {
93 ActionId::new(id_val)
94 }
95
96 fn id(&self) -> &Self::IdType {
97 self.as_ref().id()
98 }
99}
100
101#[cfg(test)]
103pub fn test_action_setup<'a>() -> (Step, StateData, stepflow_base::ObjectStore<Box<dyn Var + Send + Sync>, VarId>, VarId, Box<dyn stepflow_data::value::Value>) {
104 let mut var_store: stepflow_base::ObjectStore<Box<dyn Var + Send + Sync>, VarId> = stepflow_base::ObjectStore::new();
106 let var_id = var_store.insert_new(|id| Ok(stepflow_data::var::StringVar::new(id).boxed())).unwrap();
107 let var = var_store.get(&var_id).unwrap();
108
109 let state_val = stepflow_data::value::StringValue::try_new("hi").unwrap().boxed();
111 let mut state_data = StateData::new();
112 state_data.insert(var, state_val.clone()).unwrap();
113
114 let step = Step::new(stepflow_step::StepId::new(2), None, vec![]);
115 (step, state_data, var_store, var_id, state_val)
116}
117
118#[cfg(test)]
119mod tests {
120 use stepflow_test_util::test_id;
121 use stepflow_data::{StateData, value::TrueValue};
122 use super::{ActionId, HtmlFormAction, SetDataAction, ActionResult};
123
124 #[test]
125 fn eq() {
126 let result_start = ActionResult::StartWith(TrueValue::new().boxed());
127 let result_finish = ActionResult::Finished(StateData::new());
128 let result_cannot = ActionResult::CannotFulfill;
129
130 assert_eq!(result_start, result_start);
131 assert_ne!(result_start, result_finish);
132 assert_ne!(result_start, result_cannot);
133
134 assert_eq!(result_finish, result_finish);
135 assert_ne!(result_finish, result_cannot);
136 }
137
138 #[test]
139 fn downcast() {
140 let action = HtmlFormAction::new(test_id!(ActionId), Default::default()).boxed();
141 assert!(action.is::<HtmlFormAction>());
142 assert!(!action.is::<SetDataAction>());
143 }
144}