ghost_core/recipe/
engine.rs1use anyhow::Result;
5use regex::Regex;
6use std::collections::HashMap;
7
8use super::types::{Recipe, RecipeStep};
9
10pub fn substitute(template: &str, params: &HashMap<String, String>) -> String {
12 let re = Regex::new(r"\{\{(\w+)\}\}").expect("valid regex");
14 re.replace_all(template, |caps: ®ex::Captures| {
15 let key = &caps[1];
16 params
17 .get(key)
18 .cloned()
19 .unwrap_or_else(|| format!("{{{{{key}}}}}"))
20 })
21 .to_string()
22}
23
24pub fn substitute_step(step: &RecipeStep, params: &HashMap<String, String>) -> RecipeStep {
26 RecipeStep {
27 id: step.id,
28 action: substitute(&step.action, params),
29 target: step.target.as_ref().map(|t| super::types::Locator {
30 query: t.query.as_deref().map(|s| substitute(s, params)),
31 role: t.role.as_deref().map(|s| substitute(s, params)),
32 dom_id: t.dom_id.as_deref().map(|s| substitute(s, params)),
33 dom_class: t.dom_class.as_deref().map(|s| substitute(s, params)),
34 identifier: t.identifier.as_deref().map(|s| substitute(s, params)),
35 app: t.app.as_deref().map(|s| substitute(s, params)),
36 }),
37 params: step.params.as_ref().map(|p| {
38 p.iter()
39 .map(|(k, v)| (k.clone(), substitute(v, params)))
40 .collect()
41 }),
42 wait_after: step.wait_after.clone(),
43 note: step.note.clone(),
44 on_failure: step.on_failure.clone(),
45 }
46}
47
48pub fn validate_params(recipe: &Recipe, provided: &HashMap<String, String>) -> Result<()> {
50 let Some(param_defs) = &recipe.params else {
51 return Ok(());
52 };
53 let mut missing = vec![];
54 for (name, def) in param_defs {
55 if def.required.unwrap_or(false) && !provided.contains_key(name.as_str()) {
56 missing.push(name.as_str());
57 }
58 }
59 if !missing.is_empty() {
60 anyhow::bail!("Missing required params: {}", missing.join(", "));
61 }
62 Ok(())
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn test_substitute_basic() {
71 let mut params = HashMap::new();
72 params.insert("recipient".to_string(), "alice@example.com".to_string());
73 params.insert("subject".to_string(), "Hello".to_string());
74 let s = substitute(
75 "Send email to {{recipient}} with subject {{subject}}",
76 ¶ms,
77 );
78 assert_eq!(s, "Send email to alice@example.com with subject Hello");
79 }
80
81 #[test]
82 fn test_substitute_missing_key() {
83 let params = HashMap::new();
84 let s = substitute("Hello {{name}}", ¶ms);
85 assert_eq!(s, "Hello {{name}}");
86 }
87}