Skip to main content

punch_types/
tool.rs

1use serde::{Deserialize, Serialize};
2
3/// Category of a tool (move).
4#[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    Plugin,
25    Channel,
26    SystemAutomation,
27    UiAutomation,
28    AppIntegration,
29}
30
31impl std::fmt::Display for ToolCategory {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            Self::FileSystem => write!(f, "filesystem"),
35            Self::Web => write!(f, "web"),
36            Self::Shell => write!(f, "shell"),
37            Self::Memory => write!(f, "memory"),
38            Self::Knowledge => write!(f, "knowledge"),
39            Self::Browser => write!(f, "browser"),
40            Self::Agent => write!(f, "agent"),
41            Self::Schedule => write!(f, "schedule"),
42            Self::Event => write!(f, "event"),
43            Self::Media => write!(f, "media"),
44            Self::SourceControl => write!(f, "source_control"),
45            Self::Container => write!(f, "container"),
46            Self::Data => write!(f, "data"),
47            Self::CodeAnalysis => write!(f, "code_analysis"),
48            Self::Archive => write!(f, "archive"),
49            Self::Template => write!(f, "template"),
50            Self::Crypto => write!(f, "crypto"),
51            Self::Plugin => write!(f, "plugin"),
52            Self::Channel => write!(f, "channel"),
53            Self::SystemAutomation => write!(f, "system_automation"),
54            Self::UiAutomation => write!(f, "ui_automation"),
55            Self::AppIntegration => write!(f, "app_integration"),
56        }
57    }
58}
59
60/// Definition of a tool (move) available to agents.
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct ToolDefinition {
63    /// Unique name of the tool.
64    pub name: String,
65    /// Human-readable description.
66    pub description: String,
67    /// JSON Schema describing the tool's input parameters.
68    pub input_schema: serde_json::Value,
69    /// Category this tool belongs to.
70    pub category: ToolCategory,
71}
72
73/// The result of executing a tool.
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct ToolResult {
76    /// Whether the tool execution succeeded.
77    pub success: bool,
78    /// The output content (may be structured JSON or plain text).
79    pub output: serde_json::Value,
80    /// Optional error message if the tool failed.
81    pub error: Option<String>,
82    /// Execution duration in milliseconds.
83    pub duration_ms: u64,
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_tool_category_display_all() {
92        assert_eq!(ToolCategory::FileSystem.to_string(), "filesystem");
93        assert_eq!(ToolCategory::Web.to_string(), "web");
94        assert_eq!(ToolCategory::Shell.to_string(), "shell");
95        assert_eq!(ToolCategory::Memory.to_string(), "memory");
96        assert_eq!(ToolCategory::Knowledge.to_string(), "knowledge");
97        assert_eq!(ToolCategory::Browser.to_string(), "browser");
98        assert_eq!(ToolCategory::Agent.to_string(), "agent");
99        assert_eq!(ToolCategory::Schedule.to_string(), "schedule");
100        assert_eq!(ToolCategory::Event.to_string(), "event");
101        assert_eq!(ToolCategory::Media.to_string(), "media");
102        assert_eq!(ToolCategory::SourceControl.to_string(), "source_control");
103        assert_eq!(ToolCategory::Container.to_string(), "container");
104        assert_eq!(ToolCategory::Data.to_string(), "data");
105        assert_eq!(ToolCategory::CodeAnalysis.to_string(), "code_analysis");
106        assert_eq!(ToolCategory::Archive.to_string(), "archive");
107        assert_eq!(ToolCategory::Template.to_string(), "template");
108        assert_eq!(ToolCategory::Crypto.to_string(), "crypto");
109        assert_eq!(ToolCategory::Plugin.to_string(), "plugin");
110        assert_eq!(ToolCategory::Channel.to_string(), "channel");
111        assert_eq!(
112            ToolCategory::SystemAutomation.to_string(),
113            "system_automation"
114        );
115        assert_eq!(ToolCategory::UiAutomation.to_string(), "ui_automation");
116        assert_eq!(ToolCategory::AppIntegration.to_string(), "app_integration");
117    }
118
119    #[test]
120    fn test_tool_category_serde_roundtrip() {
121        let categories = vec![
122            ToolCategory::FileSystem,
123            ToolCategory::Web,
124            ToolCategory::Shell,
125            ToolCategory::Memory,
126            ToolCategory::Knowledge,
127            ToolCategory::Browser,
128            ToolCategory::Agent,
129            ToolCategory::Schedule,
130            ToolCategory::Event,
131            ToolCategory::Media,
132            ToolCategory::SourceControl,
133            ToolCategory::Container,
134            ToolCategory::Data,
135            ToolCategory::CodeAnalysis,
136            ToolCategory::Archive,
137            ToolCategory::Template,
138            ToolCategory::Crypto,
139            ToolCategory::Plugin,
140            ToolCategory::Channel,
141            ToolCategory::SystemAutomation,
142            ToolCategory::UiAutomation,
143            ToolCategory::AppIntegration,
144        ];
145        for cat in &categories {
146            let json = serde_json::to_string(cat).expect("serialize");
147            let deser: ToolCategory = serde_json::from_str(&json).expect("deserialize");
148            assert_eq!(&deser, cat);
149        }
150    }
151
152    #[test]
153    fn test_tool_category_serde_values() {
154        assert_eq!(
155            serde_json::to_string(&ToolCategory::FileSystem).unwrap(),
156            "\"file_system\""
157        );
158        assert_eq!(
159            serde_json::to_string(&ToolCategory::SourceControl).unwrap(),
160            "\"source_control\""
161        );
162        assert_eq!(
163            serde_json::to_string(&ToolCategory::CodeAnalysis).unwrap(),
164            "\"code_analysis\""
165        );
166    }
167
168    #[test]
169    fn test_tool_definition_serde() {
170        let def = ToolDefinition {
171            name: "read_file".to_string(),
172            description: "Read a file from disk".to_string(),
173            input_schema: serde_json::json!({
174                "type": "object",
175                "properties": {
176                    "path": {"type": "string"}
177                },
178                "required": ["path"]
179            }),
180            category: ToolCategory::FileSystem,
181        };
182        let json = serde_json::to_string(&def).expect("serialize");
183        let deser: ToolDefinition = serde_json::from_str(&json).expect("deserialize");
184        assert_eq!(deser.name, "read_file");
185        assert_eq!(deser.category, ToolCategory::FileSystem);
186    }
187
188    #[test]
189    fn test_tool_result_success() {
190        let result = ToolResult {
191            success: true,
192            output: serde_json::json!({"data": "hello"}),
193            error: None,
194            duration_ms: 50,
195        };
196        let json = serde_json::to_string(&result).expect("serialize");
197        let deser: ToolResult = serde_json::from_str(&json).expect("deserialize");
198        assert!(deser.success);
199        assert!(deser.error.is_none());
200        assert_eq!(deser.duration_ms, 50);
201    }
202
203    #[test]
204    fn test_tool_result_failure() {
205        let result = ToolResult {
206            success: false,
207            output: serde_json::Value::Null,
208            error: Some("file not found".to_string()),
209            duration_ms: 5,
210        };
211        assert!(!result.success);
212        assert_eq!(result.error.as_deref(), Some("file not found"));
213    }
214
215    #[test]
216    fn test_tool_category_equality() {
217        assert_eq!(ToolCategory::Web, ToolCategory::Web);
218        assert_ne!(ToolCategory::Web, ToolCategory::Shell);
219    }
220
221    #[test]
222    fn test_tool_category_hash() {
223        let mut set = std::collections::HashSet::new();
224        set.insert(ToolCategory::Web);
225        set.insert(ToolCategory::Shell);
226        set.insert(ToolCategory::Web);
227        assert_eq!(set.len(), 2);
228    }
229}