ricecoder_tools/
result.rs1use crate::error::ToolError;
6use chrono::Utc;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ResultMetadata {
12 pub duration_ms: u64,
14 pub provider: String,
16 pub timestamp: String,
18}
19
20impl ResultMetadata {
21 pub fn new(duration_ms: u64, provider: impl Into<String>) -> Self {
23 Self {
24 duration_ms,
25 provider: provider.into(),
26 timestamp: Utc::now().to_rfc3339(),
27 }
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct ToolResult<T> {
36 pub success: bool,
38 pub data: Option<T>,
40 pub error: Option<ToolErrorInfo>,
42 pub metadata: ResultMetadata,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct ToolErrorInfo {
49 pub code: String,
51 pub message: String,
53 pub details: Option<String>,
55 pub suggestion: Option<String>,
57}
58
59impl From<&ToolError> for ToolErrorInfo {
60 fn from(err: &ToolError) -> Self {
61 Self {
62 code: err.code.clone(),
63 message: err.message.clone(),
64 details: err.details.clone(),
65 suggestion: err.suggestion.clone(),
66 }
67 }
68}
69
70impl<T> ToolResult<T> {
71 pub fn ok(data: T, duration_ms: u64, provider: impl Into<String>) -> Self {
73 Self {
74 success: true,
75 data: Some(data),
76 error: None,
77 metadata: ResultMetadata::new(duration_ms, provider),
78 }
79 }
80
81 pub fn err(error: ToolError, duration_ms: u64, provider: impl Into<String>) -> Self {
83 Self {
84 success: false,
85 data: None,
86 error: Some(ToolErrorInfo::from(&error)),
87 metadata: ResultMetadata::new(duration_ms, provider),
88 }
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_result_metadata_creation() {
98 let metadata = ResultMetadata::new(100, "builtin");
99 assert_eq!(metadata.duration_ms, 100);
100 assert_eq!(metadata.provider, "builtin");
101 assert!(!metadata.timestamp.is_empty());
102 }
103
104 #[test]
105 fn test_tool_result_ok() {
106 let result = ToolResult::ok("test data", 50, "mcp");
107 assert!(result.success);
108 assert_eq!(result.data, Some("test data"));
109 assert!(result.error.is_none());
110 assert_eq!(result.metadata.duration_ms, 50);
111 assert_eq!(result.metadata.provider, "mcp");
112 }
113
114 #[test]
115 fn test_tool_result_err() {
116 let error = ToolError::new("TEST_ERROR", "Test message");
117 let result = ToolResult::<String>::err(error, 25, "builtin");
118 assert!(!result.success);
119 assert!(result.data.is_none());
120 assert!(result.error.is_some());
121 assert_eq!(result.metadata.duration_ms, 25);
122 }
123
124 #[test]
125 fn test_tool_error_info_conversion() {
126 let error = ToolError::new("TEST_ERROR", "Test message")
127 .with_details("Details")
128 .with_suggestion("Suggestion");
129 let error_info = ToolErrorInfo::from(&error);
130 assert_eq!(error_info.code, "TEST_ERROR");
131 assert_eq!(error_info.message, "Test message");
132 assert_eq!(error_info.details, Some("Details".to_string()));
133 assert_eq!(error_info.suggestion, Some("Suggestion".to_string()));
134 }
135
136 #[test]
137 fn test_tool_result_serialization() {
138 let result = ToolResult::ok("test data", 50, "mcp");
139 let json = serde_json::to_string(&result).unwrap();
140 assert!(json.contains("\"success\":true"));
141 assert!(json.contains("\"data\":\"test data\""));
142 }
143}