zerolaunch_plugin_api/services/parameter/
default_resolver.rs1use super::types::{ParameterError, ParameterSnapshot};
4use crate::services::parameter::resolver::ParameterResolver;
5use crate::services::parameter::template_parser::{Placeholder, TemplateParser};
6
7pub struct DefaultParameterResolver;
11
12impl DefaultParameterResolver {
13 pub fn new() -> Self {
15 Self
16 }
17}
18
19impl Default for DefaultParameterResolver {
20 fn default() -> Self {
21 Self::new()
22 }
23}
24
25#[async_trait::async_trait]
26impl ParameterResolver for DefaultParameterResolver {
27 async fn resolve(
28 &self,
29 template: &str,
30 user_args: &[String],
31 snapshot: &ParameterSnapshot,
32 ) -> Result<String, ParameterError> {
33 let placeholders = TemplateParser::parse(template);
34
35 let mut result = String::new();
36 let mut user_arg_index = 0;
37
38 for placeholder in placeholders {
39 match placeholder {
40 Placeholder::Text(text) => result.push_str(&text),
41 Placeholder::UserArg => {
42 if user_arg_index < user_args.len() {
43 result.push_str(&user_args[user_arg_index]);
44 user_arg_index += 1;
45 } else {
46 return Err(ParameterError::InsufficientArguments {
47 required: user_arg_index + 1,
48 actual: user_args.len(),
49 });
50 }
51 }
52 Placeholder::System(param) => {
53 result.push_str(&snapshot.get(param.as_key()));
54 }
55 }
56 }
57
58 Ok(result)
59 }
60
61 fn count_user_parameters(&self, template: &str) -> usize {
62 TemplateParser::count_user_args(template)
63 }
64
65 fn has_system_parameters(&self, template: &str) -> bool {
66 TemplateParser::has_system_params(template)
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 fn make_snapshot(clipboard: &str, hwnd: &str, selection: &str) -> ParameterSnapshot {
75 let mut snapshot = ParameterSnapshot::empty();
76 if !clipboard.is_empty() {
77 snapshot.insert("clipboard".to_string(), clipboard.to_string());
78 }
79 if !hwnd.is_empty() {
80 snapshot.insert("hwnd".to_string(), hwnd.to_string());
81 }
82 if !selection.is_empty() {
83 snapshot.insert("selection".to_string(), selection.to_string());
84 }
85 snapshot
86 }
87
88 #[tokio::test]
89 async fn test_resolve_no_parameters() {
90 let resolver = DefaultParameterResolver::new();
91 let snapshot = make_snapshot("test", "12345", "");
92 let result = resolver.resolve("notepad.exe", &[], &snapshot).await;
93 assert_eq!(result.unwrap(), "notepad.exe");
94 }
95
96 #[tokio::test]
97 async fn test_resolve_positional_parameters() {
98 let resolver = DefaultParameterResolver::new();
99 let snapshot = ParameterSnapshot::empty();
100 let result = resolver
101 .resolve("cmd /c echo {}", &["hello".to_string()], &snapshot)
102 .await;
103 assert_eq!(result.unwrap(), "cmd /c echo hello");
104 }
105
106 #[tokio::test]
107 async fn test_resolve_clipboard_parameter() {
108 let resolver = DefaultParameterResolver::new();
109 let snapshot = make_snapshot("clipboard_content", "", "");
110 let result = resolver.resolve("notepad {clip}", &[], &snapshot).await;
111 assert_eq!(result.unwrap(), "notepad clipboard_content");
112 }
113
114 #[tokio::test]
115 async fn test_resolve_hwnd_parameter() {
116 let resolver = DefaultParameterResolver::new();
117 let snapshot = make_snapshot("", "98765", "");
118 let result = resolver.resolve("tool --hwnd {hwnd}", &[], &snapshot).await;
119 assert_eq!(result.unwrap(), "tool --hwnd 98765");
120 }
121
122 #[tokio::test]
123 async fn test_resolve_selection_parameter() {
124 let resolver = DefaultParameterResolver::new();
125 let snapshot = make_snapshot("", "", "selected text");
126 let result = resolver
127 .resolve("translate {selection}", &[], &snapshot)
128 .await;
129 assert_eq!(result.unwrap(), "translate selected text");
130 }
131
132 #[tokio::test]
133 async fn test_resolve_mixed_parameters() {
134 let resolver = DefaultParameterResolver::new();
135 let snapshot = make_snapshot("clip_data", "54321", "selected");
136 let result = resolver
137 .resolve(
138 "program {} {clip} {} {hwnd} {selection}",
139 &["arg1".to_string(), "arg2".to_string()],
140 &snapshot,
141 )
142 .await;
143 assert_eq!(
144 result.unwrap(),
145 "program arg1 clip_data arg2 54321 selected"
146 );
147 }
148
149 #[tokio::test]
150 async fn test_argument_count_mismatch() {
151 let resolver = DefaultParameterResolver::new();
152 let snapshot = ParameterSnapshot::empty();
153 let result = resolver
154 .resolve("program {} {}", &["arg1".to_string()], &snapshot)
155 .await;
156 assert!(result.is_err());
157 }
158
159 #[test]
160 fn test_count_user_parameters() {
161 let resolver = DefaultParameterResolver::new();
162 assert_eq!(resolver.count_user_parameters("program {}"), 1);
163 assert_eq!(resolver.count_user_parameters("program {} {}"), 2);
164 assert_eq!(resolver.count_user_parameters("program {clip} {hwnd}"), 0);
165 assert_eq!(resolver.count_user_parameters("program {} {clip} {}"), 2);
166 }
167
168 #[test]
169 fn test_has_system_parameters() {
170 let resolver = DefaultParameterResolver::new();
171 assert!(resolver.has_system_parameters("program {clip}"));
172 assert!(resolver.has_system_parameters("program {hwnd}"));
173 assert!(resolver.has_system_parameters("program {selection}"));
174 assert!(!resolver.has_system_parameters("program {}"));
175 assert!(!resolver.has_system_parameters("program"));
176 }
177}