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