Skip to main content

statespace_tool_runtime/
protocol.rs

1//! Tool execution request/response protocol.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Deserialize)]
7pub struct ActionRequest {
8    pub command: Vec<String>,
9    #[serde(default)]
10    pub args: HashMap<String, String>,
11    #[serde(default)]
12    pub env: HashMap<String, String>,
13}
14
15impl ActionRequest {
16    /// # Errors
17    ///
18    /// Returns an error when the command is empty.
19    pub fn validate(&self) -> Result<(), String> {
20        if self.command.is_empty() {
21            return Err("command cannot be empty".to_string());
22        }
23        Ok(())
24    }
25}
26
27#[derive(Debug, Serialize)]
28pub struct ActionResponse {
29    pub stdout: String,
30    pub stderr: String,
31    pub returncode: i32,
32}
33
34impl ActionResponse {
35    #[must_use]
36    pub const fn success(output: String) -> Self {
37        Self {
38            stdout: output,
39            stderr: String::new(),
40            returncode: 0,
41        }
42    }
43
44    #[must_use]
45    pub const fn error(message: String) -> Self {
46        Self {
47            stdout: String::new(),
48            stderr: message,
49            returncode: 1,
50        }
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn test_action_request_validation() {
60        let valid = ActionRequest {
61            command: vec!["ls".to_string()],
62            args: HashMap::new(),
63            env: HashMap::new(),
64        };
65        assert!(valid.validate().is_ok());
66
67        let invalid = ActionRequest {
68            command: vec![],
69            args: HashMap::new(),
70            env: HashMap::new(),
71        };
72        assert!(invalid.validate().is_err());
73    }
74
75    #[test]
76    fn test_action_response() {
77        let success = ActionResponse::success("file1.md\nfile2.md".to_string());
78        assert_eq!(success.returncode, 0);
79        assert_eq!(success.stdout, "file1.md\nfile2.md");
80        assert_eq!(success.stderr, "");
81
82        let error = ActionResponse::error("command not found".to_string());
83        assert_eq!(error.returncode, 1);
84        assert_eq!(error.stdout, "");
85        assert_eq!(error.stderr, "command not found");
86    }
87}