1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5#[serde(rename_all = "snake_case")]
6pub enum ToolCategory {
7 FileSystem,
8 Web,
9 Shell,
10 Memory,
11 Knowledge,
12 Browser,
13 Agent,
14 Schedule,
15 Event,
16 Media,
17 SourceControl,
18 Container,
19 Data,
20 CodeAnalysis,
21 Archive,
22 Template,
23 Crypto,
24}
25
26impl std::fmt::Display for ToolCategory {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 match self {
29 Self::FileSystem => write!(f, "filesystem"),
30 Self::Web => write!(f, "web"),
31 Self::Shell => write!(f, "shell"),
32 Self::Memory => write!(f, "memory"),
33 Self::Knowledge => write!(f, "knowledge"),
34 Self::Browser => write!(f, "browser"),
35 Self::Agent => write!(f, "agent"),
36 Self::Schedule => write!(f, "schedule"),
37 Self::Event => write!(f, "event"),
38 Self::Media => write!(f, "media"),
39 Self::SourceControl => write!(f, "source_control"),
40 Self::Container => write!(f, "container"),
41 Self::Data => write!(f, "data"),
42 Self::CodeAnalysis => write!(f, "code_analysis"),
43 Self::Archive => write!(f, "archive"),
44 Self::Template => write!(f, "template"),
45 Self::Crypto => write!(f, "crypto"),
46 }
47 }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct ToolDefinition {
53 pub name: String,
55 pub description: String,
57 pub input_schema: serde_json::Value,
59 pub category: ToolCategory,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct ToolResult {
66 pub success: bool,
68 pub output: serde_json::Value,
70 pub error: Option<String>,
72 pub duration_ms: u64,
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_tool_category_display_all() {
82 assert_eq!(ToolCategory::FileSystem.to_string(), "filesystem");
83 assert_eq!(ToolCategory::Web.to_string(), "web");
84 assert_eq!(ToolCategory::Shell.to_string(), "shell");
85 assert_eq!(ToolCategory::Memory.to_string(), "memory");
86 assert_eq!(ToolCategory::Knowledge.to_string(), "knowledge");
87 assert_eq!(ToolCategory::Browser.to_string(), "browser");
88 assert_eq!(ToolCategory::Agent.to_string(), "agent");
89 assert_eq!(ToolCategory::Schedule.to_string(), "schedule");
90 assert_eq!(ToolCategory::Event.to_string(), "event");
91 assert_eq!(ToolCategory::Media.to_string(), "media");
92 assert_eq!(ToolCategory::SourceControl.to_string(), "source_control");
93 assert_eq!(ToolCategory::Container.to_string(), "container");
94 assert_eq!(ToolCategory::Data.to_string(), "data");
95 assert_eq!(ToolCategory::CodeAnalysis.to_string(), "code_analysis");
96 assert_eq!(ToolCategory::Archive.to_string(), "archive");
97 assert_eq!(ToolCategory::Template.to_string(), "template");
98 assert_eq!(ToolCategory::Crypto.to_string(), "crypto");
99 }
100
101 #[test]
102 fn test_tool_category_serde_roundtrip() {
103 let categories = vec![
104 ToolCategory::FileSystem,
105 ToolCategory::Web,
106 ToolCategory::Shell,
107 ToolCategory::Memory,
108 ToolCategory::Knowledge,
109 ToolCategory::Browser,
110 ToolCategory::Agent,
111 ToolCategory::Schedule,
112 ToolCategory::Event,
113 ToolCategory::Media,
114 ToolCategory::SourceControl,
115 ToolCategory::Container,
116 ToolCategory::Data,
117 ToolCategory::CodeAnalysis,
118 ToolCategory::Archive,
119 ToolCategory::Template,
120 ToolCategory::Crypto,
121 ];
122 for cat in &categories {
123 let json = serde_json::to_string(cat).expect("serialize");
124 let deser: ToolCategory = serde_json::from_str(&json).expect("deserialize");
125 assert_eq!(&deser, cat);
126 }
127 }
128
129 #[test]
130 fn test_tool_category_serde_values() {
131 assert_eq!(
132 serde_json::to_string(&ToolCategory::FileSystem).unwrap(),
133 "\"file_system\""
134 );
135 assert_eq!(
136 serde_json::to_string(&ToolCategory::SourceControl).unwrap(),
137 "\"source_control\""
138 );
139 assert_eq!(
140 serde_json::to_string(&ToolCategory::CodeAnalysis).unwrap(),
141 "\"code_analysis\""
142 );
143 }
144
145 #[test]
146 fn test_tool_definition_serde() {
147 let def = ToolDefinition {
148 name: "read_file".to_string(),
149 description: "Read a file from disk".to_string(),
150 input_schema: serde_json::json!({
151 "type": "object",
152 "properties": {
153 "path": {"type": "string"}
154 },
155 "required": ["path"]
156 }),
157 category: ToolCategory::FileSystem,
158 };
159 let json = serde_json::to_string(&def).expect("serialize");
160 let deser: ToolDefinition = serde_json::from_str(&json).expect("deserialize");
161 assert_eq!(deser.name, "read_file");
162 assert_eq!(deser.category, ToolCategory::FileSystem);
163 }
164
165 #[test]
166 fn test_tool_result_success() {
167 let result = ToolResult {
168 success: true,
169 output: serde_json::json!({"data": "hello"}),
170 error: None,
171 duration_ms: 50,
172 };
173 let json = serde_json::to_string(&result).expect("serialize");
174 let deser: ToolResult = serde_json::from_str(&json).expect("deserialize");
175 assert!(deser.success);
176 assert!(deser.error.is_none());
177 assert_eq!(deser.duration_ms, 50);
178 }
179
180 #[test]
181 fn test_tool_result_failure() {
182 let result = ToolResult {
183 success: false,
184 output: serde_json::Value::Null,
185 error: Some("file not found".to_string()),
186 duration_ms: 5,
187 };
188 assert!(!result.success);
189 assert_eq!(result.error.as_deref(), Some("file not found"));
190 }
191
192 #[test]
193 fn test_tool_category_equality() {
194 assert_eq!(ToolCategory::Web, ToolCategory::Web);
195 assert_ne!(ToolCategory::Web, ToolCategory::Shell);
196 }
197
198 #[test]
199 fn test_tool_category_hash() {
200 let mut set = std::collections::HashSet::new();
201 set.insert(ToolCategory::Web);
202 set.insert(ToolCategory::Shell);
203 set.insert(ToolCategory::Web);
204 assert_eq!(set.len(), 2);
205 }
206}