claude_codes/io/
claude_input.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use uuid::Uuid;
4
5use super::content_blocks::{ContentBlock, ImageBlock, ImageSource, TextBlock};
6use super::control::{ControlRequest, ControlResponse};
7use super::message_types::{MessageContent, UserMessage};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(tag = "type", rename_all = "snake_case")]
12pub enum ClaudeInput {
13 User(UserMessage),
15
16 ControlRequest(ControlRequest),
18
19 ControlResponse(ControlResponse),
21
22 #[serde(untagged)]
24 Raw(Value),
25}
26
27impl ClaudeInput {
28 pub fn user_message(text: impl Into<String>, session_id: Uuid) -> Self {
30 ClaudeInput::User(UserMessage {
31 message: MessageContent {
32 role: super::MessageRole::User,
33 content: vec![ContentBlock::Text(TextBlock {
34 text: text.into(),
35 citations: Vec::new(),
36 })],
37 },
38 session_id: Some(session_id),
39 parent_tool_use_id: None,
40 uuid: None,
41 timestamp: None,
42 tool_use_result: None,
43 subagent_type: None,
44 task_description: None,
45 })
46 }
47
48 pub fn user_message_blocks(blocks: Vec<ContentBlock>, session_id: Uuid) -> Self {
50 ClaudeInput::User(UserMessage {
51 message: MessageContent {
52 role: super::MessageRole::User,
53 content: blocks,
54 },
55 session_id: Some(session_id),
56 parent_tool_use_id: None,
57 uuid: None,
58 timestamp: None,
59 tool_use_result: None,
60 subagent_type: None,
61 task_description: None,
62 })
63 }
64
65 pub fn interrupt() -> Self {
71 ClaudeInput::Raw(serde_json::to_value(super::SDKControlInterruptRequest::new()).unwrap())
72 }
73
74 pub fn user_message_with_image(
77 image_data: String,
78 media_type: super::MediaType,
79 text: Option<String>,
80 session_id: Uuid,
81 ) -> Result<Self, String> {
82 match &media_type {
84 super::MediaType::Jpeg
85 | super::MediaType::Png
86 | super::MediaType::Gif
87 | super::MediaType::Webp => {}
88 other => {
89 return Err(format!(
90 "Invalid media type '{}'. Only JPEG, PNG, GIF, and WebP are supported.",
91 other
92 ));
93 }
94 }
95
96 let mut blocks = vec![ContentBlock::Image(ImageBlock {
97 source: ImageSource {
98 source_type: super::ImageSourceType::Base64,
99 media_type,
100 data: image_data,
101 },
102 })];
103
104 if let Some(text_content) = text {
105 blocks.push(ContentBlock::Text(TextBlock {
106 text: text_content,
107 citations: Vec::new(),
108 }));
109 }
110
111 Ok(Self::user_message_blocks(blocks, session_id))
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_serialize_user_message() {
121 let session_uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
122 let input = ClaudeInput::user_message("Hello, Claude!", session_uuid);
123 let json = serde_json::to_string(&input).unwrap();
124 assert!(json.contains("\"type\":\"user\""));
125 assert!(json.contains("\"role\":\"user\""));
126 assert!(json.contains("\"text\":\"Hello, Claude!\""));
127 assert!(json.contains("550e8400-e29b-41d4-a716-446655440000"));
128 }
129}