stepflow_action/action/
action_string_template.rs

1use std::collections::HashMap;
2
3use stepflow_base::{ObjectStoreFiltered, ObjectStoreContent};
4use stepflow_data::{StateDataFiltered, value::StringValue, var::{Var, VarId}};
5use super::{ActionResult, Step, Action, ActionId};
6use crate::{render_template, EscapedString};
7use crate::ActionError;
8
9
10
11#[derive(Debug)]
12pub struct StringTemplateAction<T> {
13  id: ActionId,
14  template_escaped: T,
15}
16
17impl<T> StringTemplateAction<T> 
18    where T: EscapedString
19{
20  /// Create a new instance.
21  ///
22  /// `template_escaped` must already be escaped. Parameters accepted within is `{{step}}`.
23  /// If the [`Step`] has a name, that will be populated. If not, it will be the [`StepId`].
24  pub fn new(id: ActionId, template_escaped: T) -> Self {
25    StringTemplateAction {
26      id,
27      template_escaped,
28    }
29  }
30
31  pub fn boxed(self) -> Box<dyn Action + Sync + Send> {
32    Box::new(self)
33  }
34}
35
36impl<T> Action for StringTemplateAction<T> 
37    where T: EscapedString
38{
39  fn id(&self) -> &ActionId {
40    &self.id
41  }
42
43  fn start(&mut self, step: &Step, step_name: Option<&str>, _step_data: &StateDataFiltered, _vars: &ObjectStoreFiltered<Box<dyn Var + Send + Sync>, VarId>)
44      -> Result<ActionResult, ActionError> 
45  {
46    let escaped_step = match step_name {
47      Some(name) => T::from_unescaped(name),
48      None => T::from_unescaped(&step.id().to_string()[..]),
49    };
50
51    let mut params: HashMap<&str, T> = HashMap::new();
52    params.insert("step", escaped_step);
53
54    let result_str = render_template::<T>(&self.template_escaped, params);
55    let string_val = StringValue::try_new(result_str).map_err(|_e| ActionError::Other)?;
56    Ok(ActionResult::StartWith(string_val.boxed()))
57  }
58}
59
60#[cfg(test)]
61mod tests {
62  use std::collections::HashSet;
63  use super::{StringTemplateAction};
64  use stepflow_base::{ObjectStoreContent, ObjectStoreFiltered};
65  use stepflow_data::{StateDataFiltered, value::{StringValue}};
66  use stepflow_test_util::test_id;
67  use super::super::{ActionResult, Action, ActionId, test_action_setup};
68  use crate::{EscapedString, UriEscapedString};
69
70
71  #[test]
72  fn basic() {
73    let (step, state_data, var_store, _var_id, _val) = test_action_setup();
74    let vars = ObjectStoreFiltered::new(&var_store, HashSet::new());
75    let step_data_filtered = StateDataFiltered::new(&state_data, HashSet::new());
76
77    let mut exec = StringTemplateAction::new(test_id!(ActionId) ,UriEscapedString::already_escaped("/test/{{step}}/uri#{{step}}".to_owned()));
78    let action_result = exec.start(&step, None, &step_data_filtered, &vars).unwrap();
79    let uri = format!("/test/{}/uri#{}", step.id(), step.id());
80    let expected_val = StringValue::try_new(uri).unwrap();
81    let expected_result = ActionResult::StartWith(expected_val.boxed());
82    assert_eq!(action_result, expected_result);
83  }
84
85  #[test]
86  fn encode_name() {
87    let (step, state_data, var_store, _var_id, _val) = test_action_setup();
88    let vars = ObjectStoreFiltered::new(&var_store, HashSet::new());
89    let step_data_filtered = StateDataFiltered::new(&state_data, HashSet::new());
90
91    let mut exec = StringTemplateAction::new(test_id!(ActionId) ,UriEscapedString::already_escaped("/test/uri/{{step}}".to_owned()));
92    let action_result = exec.start(&step, Some("/hi there?/"), &step_data_filtered, &vars).unwrap();
93    let expected_val = StringValue::try_new("/test/uri/%2Fhi%20there%3F%2F").unwrap();
94    let expected_result = ActionResult::StartWith(expected_val.boxed());
95    println!("ACTION: {:?}", action_result);
96    assert_eq!(action_result, expected_result);
97  }
98
99}