gemini_adk_rs/
instruction.rs1use regex::Regex;
7use std::sync::LazyLock;
8
9use crate::state::State;
10
11static PLACEHOLDER_RE: LazyLock<Regex> =
12 LazyLock::new(|| Regex::new(r"\{([a-zA-Z_][a-zA-Z0-9_:]*)\??\}").unwrap());
13
14pub fn inject_session_state(template: &str, state: &State) -> String {
21 PLACEHOLDER_RE
22 .replace_all(template, |caps: ®ex::Captures| {
23 let full_match = &caps[0];
24 let key = &caps[1];
25 let optional = full_match.ends_with("?}");
26
27 match state.get_raw(key) {
28 Some(value) => value_to_string(&value),
29 None => {
30 if optional {
31 String::new()
32 } else {
33 full_match.to_string()
34 }
35 }
36 }
37 })
38 .into_owned()
39}
40
41fn value_to_string(value: &serde_json::Value) -> String {
42 match value {
43 serde_json::Value::String(s) => s.clone(),
44 serde_json::Value::Null => String::new(),
45 other => other.to_string(),
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn simple_substitution() {
55 let state = State::new();
56 state.set("name", "Alice");
57 let result = inject_session_state("Hello, {name}!", &state);
58 assert_eq!(result, "Hello, Alice!");
59 }
60
61 #[test]
62 fn optional_key_present() {
63 let state = State::new();
64 state.set("title", "Dr.");
65 let result = inject_session_state("Hello, {title?} Smith!", &state);
66 assert_eq!(result, "Hello, Dr. Smith!");
67 }
68
69 #[test]
70 fn optional_key_missing() {
71 let state = State::new();
72 let result = inject_session_state("Hello, {title?}Smith!", &state);
73 assert_eq!(result, "Hello, Smith!");
74 }
75
76 #[test]
77 fn missing_required_key_left_as_is() {
78 let state = State::new();
79 let result = inject_session_state("Hello, {unknown}!", &state);
80 assert_eq!(result, "Hello, {unknown}!");
81 }
82
83 #[test]
84 fn multiple_keys() {
85 let state = State::new();
86 state.set("first", "Alice");
87 state.set("last", "Smith");
88 let result = inject_session_state("{first} {last}", &state);
89 assert_eq!(result, "Alice Smith");
90 }
91
92 #[test]
93 fn prefix_key() {
94 let state = State::new();
95 state.app().set("flag", true);
96 let result = inject_session_state("Flag is {app:flag}", &state);
97 assert_eq!(result, "Flag is true");
98 }
99
100 #[test]
101 fn no_placeholders_passthrough() {
102 let state = State::new();
103 let template = "No placeholders here.";
104 assert_eq!(inject_session_state(template, &state), template);
105 }
106
107 #[test]
108 fn numeric_value() {
109 let state = State::new();
110 state.set("count", 42);
111 let result = inject_session_state("Count: {count}", &state);
112 assert_eq!(result, "Count: 42");
113 }
114
115 #[test]
116 fn empty_template() {
117 let state = State::new();
118 assert_eq!(inject_session_state("", &state), "");
119 }
120}