claude_code_sdk_rust/internal/
parser.rs1use crate::error::{ClaudeSDKError, MessageParseError, Result};
2use crate::types::{
3 HookEventMessage, Message, MirrorErrorMessage, TaskNotificationMessage, TaskProgressMessage,
4 TaskStartedMessage, TaskUpdatedMessage,
5};
6
7const KNOWN_MESSAGE_TYPES: &[&str] = &[
8 "user",
9 "assistant",
10 "system",
11 "result",
12 "stream_event",
13 "rate_limit_event",
14];
15
16pub fn parse_message_line(line: &str) -> Result<Option<Message>> {
17 let value = serde_json::from_str::<serde_json::Value>(line)?;
18 parse_message_value(value)
19}
20
21pub fn parse_message_value(value: serde_json::Value) -> Result<Option<Message>> {
22 let message_type = value.get("type").and_then(|v| v.as_str()).ok_or_else(|| {
23 let data = value.as_object().cloned();
24 let mut error = MessageParseError::new("Message missing 'type' field");
25 if let Some(data) = data {
26 error = error.with_data(data);
27 }
28 ClaudeSDKError::MessageParse(error)
29 })?;
30
31 if !KNOWN_MESSAGE_TYPES.contains(&message_type) {
32 return Ok(None);
33 }
34
35 if message_type == "system" {
36 return parse_system_message_value(value);
37 }
38
39 match serde_json::from_value::<Message>(value.clone()) {
40 Ok(message) => Ok(Some(message)),
41 Err(err) => Err(parse_error_with_payload(err, &value)),
42 }
43}
44
45fn parse_error_with_payload(err: serde_json::Error, value: &serde_json::Value) -> ClaudeSDKError {
48 let payload = value.to_string();
49 let payload = if payload.len() > 600 {
50 let cut = payload
51 .char_indices()
52 .take_while(|(idx, _)| *idx <= 600)
53 .last()
54 .map(|(idx, ch)| idx + ch.len_utf8())
55 .unwrap_or(payload.len());
56 format!("{}...", &payload[..cut])
57 } else {
58 payload
59 };
60 let mut error =
61 MessageParseError::new(format!("Failed to parse CLI message: {err}; payload: {payload}"));
62 if let Some(data) = value.as_object() {
63 error = error.with_data(data.clone());
64 }
65 ClaudeSDKError::MessageParse(error)
66}
67
68fn parse_system_message_value(value: serde_json::Value) -> Result<Option<Message>> {
69 let subtype = value.get("subtype").and_then(|v| v.as_str());
70 match subtype {
71 Some("task_started") => parse_task_started(value)
72 .map(Message::TaskStartedMsg)
73 .map(Some),
74 Some("task_progress") => parse_task_progress(value)
75 .map(Message::TaskProgressMsg)
76 .map(Some),
77 Some("task_notification") => parse_task_notification(value)
78 .map(Message::TaskNotificationMsg)
79 .map(Some),
80 Some("task_updated") => parse_task_updated(value)
81 .map(Message::TaskUpdatedMsg)
82 .map(Some),
83 Some("hook_started" | "hook_response") => {
84 parse_hook_event(value).map(Message::HookEventMsg).map(Some)
85 }
86 Some("mirror_error") => parse_mirror_error(value)
87 .map(Message::MirrorErrorMsg)
88 .map(Some),
89 _ => serde_json::from_value::<Message>(value)
90 .map(Some)
91 .map_err(ClaudeSDKError::Serialization),
92 }
93}
94
95fn parse_mirror_error(value: serde_json::Value) -> Result<MirrorErrorMessage> {
96 let mut data = value.as_object().cloned().ok_or_else(|| {
97 ClaudeSDKError::MessageParse(MessageParseError::new("System message must be an object"))
98 })?;
99 data.remove("type");
100 let key = data.get("key").and_then(|value| value.as_object()).cloned();
101 let error = data
102 .get("error")
103 .and_then(|value| value.as_str())
104 .unwrap_or_default()
105 .to_string();
106 Ok(MirrorErrorMessage { key, error, data })
107}
108
109fn parse_task_started(value: serde_json::Value) -> Result<TaskStartedMessage> {
110 serde_json::from_value::<TaskStartedMessage>(strip_system_fields(value)?)
111 .map_err(ClaudeSDKError::Serialization)
112}
113
114fn parse_task_progress(value: serde_json::Value) -> Result<TaskProgressMessage> {
115 serde_json::from_value::<TaskProgressMessage>(strip_system_fields(value)?)
116 .map_err(ClaudeSDKError::Serialization)
117}
118
119fn parse_task_notification(value: serde_json::Value) -> Result<TaskNotificationMessage> {
120 serde_json::from_value::<TaskNotificationMessage>(strip_system_fields(value)?)
121 .map_err(ClaudeSDKError::Serialization)
122}
123
124fn parse_task_updated(value: serde_json::Value) -> Result<TaskUpdatedMessage> {
130 let data = value.as_object().ok_or_else(|| {
131 ClaudeSDKError::MessageParse(MessageParseError::new("System message must be an object"))
132 })?;
133 let task_id = data
134 .get("task_id")
135 .and_then(|v| v.as_str())
136 .unwrap_or_default()
137 .to_string();
138 let patch = data
139 .get("patch")
140 .and_then(|v| v.as_object())
141 .cloned()
142 .unwrap_or_default();
143 let status = patch.get("status").and_then(|v| {
144 serde_json::from_value::<crate::types::TaskUpdatedStatus>(v.clone()).ok()
145 });
146 let session_id = data
147 .get("session_id")
148 .and_then(|v| v.as_str())
149 .map(|s| s.to_string());
150 let uuid = data
151 .get("uuid")
152 .and_then(|v| v.as_str())
153 .map(|s| s.to_string());
154 Ok(TaskUpdatedMessage {
155 task_id,
156 patch,
157 status,
158 session_id,
159 uuid,
160 })
161}
162
163fn parse_hook_event(value: serde_json::Value) -> Result<HookEventMessage> {
164 let mut data = value.as_object().cloned().ok_or_else(|| {
165 ClaudeSDKError::MessageParse(MessageParseError::new("System message must be an object"))
166 })?;
167 let subtype = data
168 .get("subtype")
169 .and_then(|value| value.as_str())
170 .unwrap_or_default()
171 .to_string();
172 let hook_event_name = data
173 .get("hook_event")
174 .or_else(|| data.get("hook_name"))
175 .and_then(|value| value.as_str())
176 .map(ToString::to_string);
177 let session_id = data
178 .get("session_id")
179 .and_then(|value| value.as_str())
180 .map(ToString::to_string);
181 let uuid = data
182 .get("uuid")
183 .and_then(|value| value.as_str())
184 .map(ToString::to_string);
185 data.remove("type");
186 Ok(HookEventMessage {
187 subtype,
188 hook_event_name,
189 session_id,
190 uuid,
191 data,
192 })
193}
194
195fn strip_system_fields(value: serde_json::Value) -> Result<serde_json::Value> {
196 let mut data = value.as_object().cloned().ok_or_else(|| {
197 ClaudeSDKError::MessageParse(MessageParseError::new("System message must be an object"))
198 })?;
199 data.remove("type");
200 data.remove("subtype");
201 Ok(serde_json::Value::Object(data))
202}