Skip to main content

zerolaunch_plugin_api/services/parameter/
default_resolver.rs

1//! 默认参数解析器实现
2
3use super::types::{ParameterError, ParameterSnapshot};
4use crate::services::parameter::resolver::ParameterResolver;
5use crate::services::parameter::template_parser::{Placeholder, TemplateParser};
6
7/// 默认参数解析器实现
8///
9/// 使用 TemplateParser 解析模板占位符,从快照和用户参数中填充值。
10pub struct DefaultParameterResolver;
11
12impl DefaultParameterResolver {
13    /// 创建默认参数解析器
14    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}