1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(tag = "type")]
7pub enum ClientMessage {
8 #[serde(rename = "message")]
10 Message { content: String },
11
12 #[serde(rename = "command")]
14 Command { name: String, args: String },
15
16 #[serde(rename = "cancel")]
18 Cancel,
19
20 #[serde(rename = "status")]
22 Status,
23
24 #[serde(rename = "history")]
26 History,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(tag = "type")]
32pub enum ServerMessage {
33 #[serde(rename = "thinking")]
35 Thinking { content: String },
36
37 #[serde(rename = "text")]
39 Text { content: String },
40
41 #[serde(rename = "tool_use_start")]
43 ToolUseStart { tool_name: String },
44
45 #[serde(rename = "tool_use_delta")]
47 ToolUseDelta(String),
48
49 #[serde(rename = "tool_use")]
51 ToolUse {
52 tool_name: String,
53 tool_id: String,
54 input: Value,
55 },
56
57 #[serde(rename = "tool_result")]
59 ToolResult { tool_id: String, result: String },
60
61 #[serde(rename = "tool_result_delta")]
63 ToolResultDelta { tool_id: String, delta: String },
64
65 #[serde(rename = "usage")]
67 Usage {
68 input_tokens: u64,
69 output_tokens: u64,
70 #[serde(default, skip_serializing_if = "Option::is_none")]
72 cache_creation_5m: Option<u64>,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
74 cache_creation_1h: Option<u64>,
75 },
76
77 #[serde(rename = "done")]
79 Done,
80
81 #[serde(rename = "notice")]
84 Notice { text: String },
85
86 #[serde(rename = "error")]
88 Error { message: String },
89
90 #[serde(rename = "system")]
92 System { message: String },
93
94 #[serde(rename = "history")]
96 HistoryResponse { messages: Vec<HistoryEntry> },
97
98 #[serde(rename = "status")]
100 StatusResponse {
101 model: String,
102 thinking: String,
103 streaming: bool,
104 session_id: String,
105 total_input_tokens: u64,
106 total_output_tokens: u64,
107 session_cost: f64,
108 connected_clients: usize,
109 },
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
114#[serde(tag = "role")]
115pub enum HistoryEntry {
116 #[serde(rename = "user")]
117 User { content: String, time: String },
118 #[serde(rename = "thinking")]
119 Thinking { content: String, time: String },
120 #[serde(rename = "text")]
121 Text { content: String, time: String },
122 #[serde(rename = "tool_use")]
123 ToolUse {
124 tool_name: String,
125 input: String,
126 time: String,
127 },
128 #[serde(rename = "tool_result")]
129 ToolResult { result: String, time: String },
130 #[serde(rename = "system")]
131 System { content: String, time: String },
132 #[serde(rename = "error")]
133 Error { content: String, time: String },
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use serde_json::json;
140
141 #[test]
142 fn test_client_message_message_roundtrip() {
143 let msg = ClientMessage::Message {
144 content: "Hello, world!".to_string(),
145 };
146
147 let json_value = serde_json::to_value(&msg).unwrap();
149
150 assert_eq!(json_value["type"], "message");
152 assert_eq!(json_value["content"], "Hello, world!");
153
154 let deserialized: ClientMessage = serde_json::from_value(json_value).unwrap();
156 match deserialized {
157 ClientMessage::Message { content } => {
158 assert_eq!(content, "Hello, world!");
159 }
160 _ => panic!("Expected Message variant"),
161 }
162 }
163
164 #[test]
165 fn test_client_message_cancel_roundtrip() {
166 let msg = ClientMessage::Cancel;
167
168 let json_value = serde_json::to_value(&msg).unwrap();
170
171 assert_eq!(json_value["type"], "cancel");
173
174 let deserialized: ClientMessage = serde_json::from_value(json_value).unwrap();
176 matches!(deserialized, ClientMessage::Cancel);
177 }
178
179 #[test]
180 fn test_client_message_command_roundtrip() {
181 let msg = ClientMessage::Command {
182 name: "clear".to_string(),
183 args: "".to_string(),
184 };
185
186 let json_value = serde_json::to_value(&msg).unwrap();
188
189 assert_eq!(json_value["type"], "command");
191 assert_eq!(json_value["name"], "clear");
192 assert_eq!(json_value["args"], "");
193
194 let deserialized: ClientMessage = serde_json::from_value(json_value).unwrap();
196 match deserialized {
197 ClientMessage::Command { name, args } => {
198 assert_eq!(name, "clear");
199 assert_eq!(args, "");
200 }
201 _ => panic!("Expected Command variant"),
202 }
203 }
204
205 #[test]
206 fn test_server_message_text_roundtrip() {
207 let msg = ServerMessage::Text {
208 content: "Hello from server!".to_string(),
209 };
210
211 let json_value = serde_json::to_value(&msg).unwrap();
213
214 assert_eq!(json_value["type"], "text");
216 assert_eq!(json_value["content"], "Hello from server!");
217
218 let deserialized: ServerMessage = serde_json::from_value(json_value).unwrap();
220 match deserialized {
221 ServerMessage::Text { content } => {
222 assert_eq!(content, "Hello from server!");
223 }
224 _ => panic!("Expected Text variant"),
225 }
226 }
227
228 #[test]
229 fn test_server_message_done_roundtrip() {
230 let msg = ServerMessage::Done;
231
232 let json_value = serde_json::to_value(&msg).unwrap();
234
235 assert_eq!(json_value["type"], "done");
237
238 let deserialized: ServerMessage = serde_json::from_value(json_value).unwrap();
240 matches!(deserialized, ServerMessage::Done);
241 }
242
243 #[test]
244 fn test_server_message_error_roundtrip() {
245 let msg = ServerMessage::Error {
246 message: "Something went wrong".to_string(),
247 };
248
249 let json_value = serde_json::to_value(&msg).unwrap();
251
252 assert_eq!(json_value["type"], "error");
254 assert_eq!(json_value["message"], "Something went wrong");
255
256 let deserialized: ServerMessage = serde_json::from_value(json_value).unwrap();
258 match deserialized {
259 ServerMessage::Error { message } => {
260 assert_eq!(message, "Something went wrong");
261 }
262 _ => panic!("Expected Error variant"),
263 }
264 }
265
266 #[test]
267 fn test_server_message_tool_use_roundtrip() {
268 let msg = ServerMessage::ToolUse {
269 tool_name: "execute_bash".to_string(),
270 tool_id: "tool_123".to_string(),
271 input: json!({"command": "ls -la", "timeout": 30}),
272 };
273
274 let json_value = serde_json::to_value(&msg).unwrap();
276
277 assert_eq!(json_value["type"], "tool_use");
279 assert_eq!(json_value["tool_name"], "execute_bash");
280 assert_eq!(json_value["tool_id"], "tool_123");
281 assert_eq!(json_value["input"]["command"], "ls -la");
282 assert_eq!(json_value["input"]["timeout"], 30);
283
284 let deserialized: ServerMessage = serde_json::from_value(json_value).unwrap();
286 match deserialized {
287 ServerMessage::ToolUse {
288 tool_name,
289 tool_id,
290 input,
291 } => {
292 assert_eq!(tool_name, "execute_bash");
293 assert_eq!(tool_id, "tool_123");
294 assert_eq!(input["command"], "ls -la");
295 assert_eq!(input["timeout"], 30);
296 }
297 _ => panic!("Expected ToolUse variant"),
298 }
299 }
300
301 #[test]
302 fn test_server_message_usage_roundtrip() {
303 let msg = ServerMessage::Usage {
304 input_tokens: 150,
305 output_tokens: 75,
306 cache_creation_5m: None,
307 cache_creation_1h: None,
308 };
309
310 let json_value = serde_json::to_value(&msg).unwrap();
312
313 assert_eq!(json_value["type"], "usage");
315 assert_eq!(json_value["input_tokens"], 150);
316 assert_eq!(json_value["output_tokens"], 75);
317 assert!(json_value.get("cache_creation_5m").is_none());
320 assert!(json_value.get("cache_creation_1h").is_none());
321
322 let deserialized: ServerMessage = serde_json::from_value(json_value).unwrap();
324 match deserialized {
325 ServerMessage::Usage {
326 input_tokens,
327 output_tokens,
328 cache_creation_5m,
329 cache_creation_1h,
330 } => {
331 assert_eq!(input_tokens, 150);
332 assert_eq!(output_tokens, 75);
333 assert_eq!(cache_creation_5m, None);
334 assert_eq!(cache_creation_1h, None);
335 }
336 _ => panic!("Expected Usage variant"),
337 }
338 }
339
340 #[test]
341 fn test_history_entry_user_roundtrip() {
342 let entry = HistoryEntry::User {
343 content: "User message".to_string(),
344 time: "2023-10-01T12:00:00Z".to_string(),
345 };
346
347 let json_value = serde_json::to_value(&entry).unwrap();
349
350 assert_eq!(json_value["role"], "user");
352 assert_eq!(json_value["content"], "User message");
353 assert_eq!(json_value["time"], "2023-10-01T12:00:00Z");
354
355 let deserialized: HistoryEntry = serde_json::from_value(json_value).unwrap();
357 match deserialized {
358 HistoryEntry::User { content, time } => {
359 assert_eq!(content, "User message");
360 assert_eq!(time, "2023-10-01T12:00:00Z");
361 }
362 _ => panic!("Expected User variant"),
363 }
364 }
365}