claudius/types/
tool_result_block.rs1use serde::{Deserialize, Serialize};
2
3use crate::types::{CacheControlEphemeral, Content};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11#[serde(tag = "type")]
12#[serde(rename = "tool_result")]
13pub struct ToolResultBlock {
14 #[serde(rename = "tool_use_id")]
18 pub tool_use_id: String,
19
20 #[serde(skip_serializing_if = "Option::is_none")]
25 pub cache_control: Option<CacheControlEphemeral>,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
32 pub content: Option<ToolResultBlockContent>,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
39 pub is_error: Option<bool>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
44#[serde(untagged)]
45pub enum ToolResultBlockContent {
46 String(String),
48
49 Array(Vec<Content>),
51}
52
53impl From<String> for ToolResultBlockContent {
54 fn from(value: String) -> Self {
55 ToolResultBlockContent::String(value)
56 }
57}
58
59impl From<&str> for ToolResultBlockContent {
60 fn from(value: &str) -> Self {
61 ToolResultBlockContent::String(value.to_string())
62 }
63}
64
65impl ToolResultBlock {
66 pub fn new(tool_use_id: String) -> Self {
68 Self {
69 tool_use_id,
70 cache_control: None,
71 content: None,
72 is_error: None,
73 }
74 }
75
76 pub fn with_cache_control(mut self, cache_control: CacheControlEphemeral) -> Self {
78 self.cache_control = Some(cache_control);
79 self
80 }
81
82 pub fn with_string_content(mut self, content: String) -> Self {
84 self.content = Some(ToolResultBlockContent::String(content));
85 self
86 }
87
88 pub fn with_array_content(mut self, content: Vec<Content>) -> Self {
90 self.content = Some(ToolResultBlockContent::Array(content));
91 self
92 }
93
94 pub fn with_text_content(mut self, text: crate::types::TextBlock) -> Self {
96 let content = match self.content {
97 Some(ToolResultBlockContent::Array(mut items)) => {
98 items.push(Content::Text(text));
99 ToolResultBlockContent::Array(items)
100 }
101 Some(ToolResultBlockContent::String(s)) => ToolResultBlockContent::Array(vec![
102 Content::Text(crate::types::TextBlock::new(s)),
103 Content::Text(text),
104 ]),
105 None => ToolResultBlockContent::Array(vec![Content::Text(text)]),
106 };
107 self.content = Some(content);
108 self
109 }
110
111 pub fn with_error(mut self, is_error: bool) -> Self {
113 self.is_error = Some(is_error);
114 self
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use serde_json::{json, to_value};
122
123 #[test]
124 fn tool_result_block_with_string_content() {
125 let block = ToolResultBlock::new("tool_1".to_string())
126 .with_string_content("Result of tool execution".to_string());
127
128 let json = to_value(&block).unwrap();
129 assert_eq!(
130 json,
131 json!({
132 "tool_use_id": "tool_1",
133 "type": "tool_result",
134 "content": "Result of tool execution"
135 })
136 );
137 }
138
139 #[test]
140 fn tool_result_block_with_array_content() {
141 let text_param = crate::types::TextBlock::new("Sample text content".to_string());
142 let content = vec![Content::Text(text_param)];
143
144 let block = ToolResultBlock::new("tool_1".to_string()).with_array_content(content);
145
146 let json = to_value(&block).unwrap();
147 assert_eq!(
148 json,
149 json!({
150 "tool_use_id": "tool_1",
151 "type": "tool_result",
152 "content": [
153 {
154 "text": "Sample text content",
155 "type": "text"
156 }
157 ]
158 })
159 );
160 }
161
162 #[test]
163 fn tool_result_block_with_error() {
164 let block = ToolResultBlock::new("tool_1".to_string())
165 .with_string_content("Error executing tool".to_string())
166 .with_error(true);
167
168 let json = to_value(&block).unwrap();
169 assert_eq!(
170 json,
171 json!({
172 "tool_use_id": "tool_1",
173 "type": "tool_result",
174 "content": "Error executing tool",
175 "is_error": true
176 })
177 );
178 }
179
180 #[test]
181 fn tool_result_block_deserialization() {
182 let json = json!({
183 "tool_use_id": "tool_1",
184 "type": "tool_result",
185 "content": "Result of tool execution",
186 "is_error": false
187 });
188
189 let block: ToolResultBlock = serde_json::from_value(json).unwrap();
190 assert_eq!(block.tool_use_id, "tool_1");
191
192 match &block.content {
193 Some(ToolResultBlockContent::String(s)) => {
194 assert_eq!(s, "Result of tool execution");
195 }
196 _ => panic!("Expected String variant"),
197 }
198
199 assert_eq!(block.is_error, Some(false));
200 }
201
202 #[test]
203 fn tool_result_block_content_from_string() {
204 let content: ToolResultBlockContent = "Test content".to_string().into();
206 match content {
207 ToolResultBlockContent::String(s) => assert_eq!(s, "Test content"),
208 _ => panic!("Expected String variant"),
209 }
210
211 let content: ToolResultBlockContent = "Another test".into();
213 match content {
214 ToolResultBlockContent::String(s) => assert_eq!(s, "Another test"),
215 _ => panic!("Expected String variant"),
216 }
217 }
218}