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}
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/// Definition of a tool (move) available to agents.
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct ToolDefinition {
53    /// Unique name of the tool.
54    pub name: String,
55    /// Human-readable description.
56    pub description: String,
57    /// JSON Schema describing the tool's input parameters.
58    pub input_schema: serde_json::Value,
59    /// Category this tool belongs to.
60    pub category: ToolCategory,
61}
62
63/// The result of executing a tool.
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct ToolResult {
66    /// Whether the tool execution succeeded.
67    pub success: bool,
68    /// The output content (may be structured JSON or plain text).
69    pub output: serde_json::Value,
70    /// Optional error message if the tool failed.
71    pub error: Option<String>,
72    /// Execution duration in milliseconds.
73    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}