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