Skip to main content

smcp_computer/inputs/
model.rs

1/**
2* 文件名: model
3* 作者: JQQ
4* 创建日期: 2025/12/15
5* 最后修改日期: 2025/12/15
6* 版权: 2023 JQQ. All rights reserved.
7* 依赖: serde, async-trait
8* 描述: Inputs相关的数据模型定义
9*/
10use serde::{Deserialize, Serialize};
11use std::fmt;
12use thiserror::Error;
13
14/// 输入值 / Input value
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
16#[serde(untagged)]
17pub enum InputValue {
18    /// 字符串值 / String value
19    String(String),
20    /// 布尔值 / Boolean value
21    Bool(bool),
22    /// 数字值 / Number value
23    Number(i64),
24    /// 浮点数值 / Float value
25    Float(f64),
26}
27
28impl fmt::Display for InputValue {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self {
31            InputValue::String(s) => write!(f, "{}", s),
32            InputValue::Bool(b) => write!(f, "{}", b),
33            InputValue::Number(n) => write!(f, "{}", n),
34            InputValue::Float(fl) => write!(f, "{}", fl),
35        }
36    }
37}
38
39impl From<String> for InputValue {
40    fn from(s: String) -> Self {
41        InputValue::String(s)
42    }
43}
44
45impl From<&str> for InputValue {
46    fn from(s: &str) -> Self {
47        InputValue::String(s.to_string())
48    }
49}
50
51impl From<bool> for InputValue {
52    fn from(b: bool) -> Self {
53        InputValue::Bool(b)
54    }
55}
56
57impl From<i64> for InputValue {
58    fn from(n: i64) -> Self {
59        InputValue::Number(n)
60    }
61}
62
63impl From<f64> for InputValue {
64    fn from(f: f64) -> Self {
65        InputValue::Float(f)
66    }
67}
68
69/// 输入请求 / Input request
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
71pub struct InputRequest {
72    /// 输入ID / Input ID
73    pub id: String,
74    /// 输入类型 / Input type
75    pub input_type: InputType,
76    /// 标题 / Title
77    pub title: String,
78    /// 描述 / Description
79    pub description: String,
80    /// 默认值 / Default value
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub default: Option<InputValue>,
83    /// 是否必填 / Required
84    #[serde(default)]
85    pub required: bool,
86    /// 验证规则 / Validation rules
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub validation: Option<ValidationRule>,
89}
90
91/// 输入类型 / Input type
92#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
93#[serde(tag = "type")]
94pub enum InputType {
95    /// 字符串输入 / String input
96    String {
97        /// 是否为密码 / Whether it's a password
98        #[serde(skip_serializing_if = "Option::is_none")]
99        password: Option<bool>,
100        /// 最小长度 / Minimum length
101        #[serde(skip_serializing_if = "Option::is_none")]
102        min_length: Option<usize>,
103        /// 最大长度 / Maximum length
104        #[serde(skip_serializing_if = "Option::is_none")]
105        max_length: Option<usize>,
106    },
107    /// 选择输入 / Pick string input
108    PickString {
109        /// 选项列表 / Options list
110        options: Vec<String>,
111        /// 是否允许多选 / Allow multiple selection
112        #[serde(default)]
113        multiple: bool,
114    },
115    /// 数字输入 / Number input
116    Number {
117        /// 最小值 / Minimum value
118        #[serde(skip_serializing_if = "Option::is_none")]
119        min: Option<i64>,
120        /// 最大值 / Maximum value
121        #[serde(skip_serializing_if = "Option::is_none")]
122        max: Option<i64>,
123    },
124    /// 布尔输入 / Boolean input
125    Bool {
126        /// 真值标签 / True label
127        #[serde(skip_serializing_if = "Option::is_none")]
128        true_label: Option<String>,
129        /// 假值标签 / False label
130        #[serde(skip_serializing_if = "Option::is_none")]
131        false_label: Option<String>,
132    },
133    /// 文件路径输入 / File path input
134    FilePath {
135        /// 是否必须存在 / Must exist
136        #[serde(default)]
137        must_exist: bool,
138        /// 文件类型过滤器 / File type filter
139        #[serde(skip_serializing_if = "Option::is_none")]
140        filter: Option<String>,
141    },
142    /// 命令输入 / Command input
143    Command {
144        /// 命令 / Command
145        command: String,
146        /// 参数 / Arguments
147        #[serde(default)]
148        args: Vec<String>,
149    },
150}
151
152/// 验证规则 / Validation rule
153#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
154#[serde(tag = "type")]
155pub enum ValidationRule {
156    /// 正则表达式 / Regular expression
157    Regex {
158        /// 模式 / Pattern
159        pattern: String,
160        /// 错误消息 / Error message
161        #[serde(skip_serializing_if = "Option::is_none")]
162        message: Option<String>,
163    },
164    /// 自定义验证函数 / Custom validation function
165    Custom {
166        /// 函数名 / Function name
167        function: String,
168        /// 参数 / Parameters
169        #[serde(default)]
170        params: std::collections::HashMap<String, serde_json::Value>,
171    },
172}
173
174/// 输入响应 / Input response
175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
176pub struct InputResponse {
177    /// 输入ID / Input ID
178    pub id: String,
179    /// 输入值 / Input value
180    pub value: InputValue,
181    /// 是否取消 / Cancelled
182    #[serde(default)]
183    pub cancelled: bool,
184}
185
186/// 输入错误 / Input error
187#[derive(Debug, Error)]
188pub enum InputError {
189    /// 无效的输入类型 / Invalid input type
190    #[error("Invalid input type: {0}")]
191    InvalidType(String),
192    /// 验证失败 / Validation failed
193    #[error("Validation failed: {0}")]
194    ValidationFailed(String),
195    /// 取消输入 / Input cancelled
196    #[error("Input cancelled")]
197    Cancelled,
198    /// IO错误 / IO error
199    #[error("IO error: {0}")]
200    IoError(#[from] std::io::Error),
201    /// 超时错误 / Timeout error
202    #[error("Input timeout")]
203    Timeout,
204    /// 其他错误 / Other error
205    #[error("Other error: {0}")]
206    Other(String),
207}
208
209/// 输入结果 / Input result
210pub type InputResult<T> = Result<T, InputError>;
211
212/// 输入上下文 / Input context
213#[derive(Debug, Clone)]
214pub struct InputContext {
215    /// 服务器名称 / Server name
216    pub server_name: Option<String>,
217    /// 工具名称 / Tool name
218    pub tool_name: Option<String>,
219    /// 额外元数据 / Additional metadata
220    pub metadata: std::collections::HashMap<String, String>,
221}
222
223impl InputContext {
224    /// 创建新的输入上下文 / Create new input context
225    pub fn new() -> Self {
226        Self {
227            server_name: None,
228            tool_name: None,
229            metadata: std::collections::HashMap::new(),
230        }
231    }
232
233    /// 设置服务器名称 / Set server name
234    pub fn with_server_name(mut self, name: String) -> Self {
235        self.server_name = Some(name);
236        self
237    }
238
239    /// 设置工具名称 / Set tool name
240    pub fn with_tool_name(mut self, name: String) -> Self {
241        self.tool_name = Some(name);
242        self
243    }
244
245    /// 添加元数据 / Add metadata
246    pub fn with_metadata(mut self, key: String, value: String) -> Self {
247        self.metadata.insert(key, value);
248        self
249    }
250}
251
252impl Default for InputContext {
253    fn default() -> Self {
254        Self::new()
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261
262    #[test]
263    fn test_input_value_display() {
264        assert_eq!(InputValue::String("test".to_string()).to_string(), "test");
265        assert_eq!(InputValue::Bool(true).to_string(), "true");
266        assert_eq!(InputValue::Number(42).to_string(), "42");
267        assert_eq!(InputValue::Float(3.15).to_string(), "3.15");
268    }
269
270    #[test]
271    fn test_input_value_conversions() {
272        // 测试各种类型的转换 / Test conversions of various types
273        let string_val: InputValue = "test".into();
274        assert_eq!(string_val, InputValue::String("test".to_string()));
275
276        let bool_val: InputValue = true.into();
277        assert_eq!(bool_val, InputValue::Bool(true));
278
279        let number_val: InputValue = 42i64.into();
280        assert_eq!(number_val, InputValue::Number(42));
281
282        let float_val: InputValue = std::f64::consts::PI.into();
283        assert_eq!(float_val, InputValue::Float(std::f64::consts::PI));
284    }
285
286    #[test]
287    fn test_input_context() {
288        let ctx = InputContext::new()
289            .with_server_name("test_server".to_string())
290            .with_tool_name("test_tool".to_string())
291            .with_metadata("key1".to_string(), "value1".to_string())
292            .with_metadata("key2".to_string(), "value2".to_string());
293
294        assert_eq!(ctx.server_name, Some("test_server".to_string()));
295        assert_eq!(ctx.tool_name, Some("test_tool".to_string()));
296        assert_eq!(ctx.metadata.len(), 2);
297        assert_eq!(ctx.metadata.get("key1"), Some(&"value1".to_string()));
298        assert_eq!(ctx.metadata.get("key2"), Some(&"value2".to_string()));
299    }
300}