claude_code_sdk_rust/internal/
parser.rs1use crate::error::{ClaudeSDKError, MessageParseError, Result};
2use crate::types::{
3 HookEventMessage, Message, MirrorErrorMessage, TaskNotificationMessage, TaskProgressMessage,
4 TaskStartedMessage,
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("hook_started" | "hook_response") => {
81 parse_hook_event(value).map(Message::HookEventMsg).map(Some)
82 }
83 Some("mirror_error") => parse_mirror_error(value)
84 .map(Message::MirrorErrorMsg)
85 .map(Some),
86 _ => serde_json::from_value::<Message>(value)
87 .map(Some)
88 .map_err(ClaudeSDKError::Serialization),
89 }
90}
91
92fn parse_mirror_error(value: serde_json::Value) -> Result<MirrorErrorMessage> {
93 let mut data = value.as_object().cloned().ok_or_else(|| {
94 ClaudeSDKError::MessageParse(MessageParseError::new("System message must be an object"))
95 })?;
96 data.remove("type");
97 let key = data.get("key").and_then(|value| value.as_object()).cloned();
98 let error = data
99 .get("error")
100 .and_then(|value| value.as_str())
101 .unwrap_or_default()
102 .to_string();
103 Ok(MirrorErrorMessage { key, error, data })
104}
105
106fn parse_task_started(value: serde_json::Value) -> Result<TaskStartedMessage> {
107 serde_json::from_value::<TaskStartedMessage>(strip_system_fields(value)?)
108 .map_err(ClaudeSDKError::Serialization)
109}
110
111fn parse_task_progress(value: serde_json::Value) -> Result<TaskProgressMessage> {
112 serde_json::from_value::<TaskProgressMessage>(strip_system_fields(value)?)
113 .map_err(ClaudeSDKError::Serialization)
114}
115
116fn parse_task_notification(value: serde_json::Value) -> Result<TaskNotificationMessage> {
117 serde_json::from_value::<TaskNotificationMessage>(strip_system_fields(value)?)
118 .map_err(ClaudeSDKError::Serialization)
119}
120
121fn parse_hook_event(value: serde_json::Value) -> Result<HookEventMessage> {
122 let mut data = value.as_object().cloned().ok_or_else(|| {
123 ClaudeSDKError::MessageParse(MessageParseError::new("System message must be an object"))
124 })?;
125 let subtype = data
126 .get("subtype")
127 .and_then(|value| value.as_str())
128 .unwrap_or_default()
129 .to_string();
130 let hook_event_name = data
131 .get("hook_event")
132 .or_else(|| data.get("hook_name"))
133 .and_then(|value| value.as_str())
134 .map(ToString::to_string);
135 let session_id = data
136 .get("session_id")
137 .and_then(|value| value.as_str())
138 .map(ToString::to_string);
139 let uuid = data
140 .get("uuid")
141 .and_then(|value| value.as_str())
142 .map(ToString::to_string);
143 data.remove("type");
144 Ok(HookEventMessage {
145 subtype,
146 hook_event_name,
147 session_id,
148 uuid,
149 data,
150 })
151}
152
153fn strip_system_fields(value: serde_json::Value) -> Result<serde_json::Value> {
154 let mut data = value.as_object().cloned().ok_or_else(|| {
155 ClaudeSDKError::MessageParse(MessageParseError::new("System message must be an object"))
156 })?;
157 data.remove("type");
158 data.remove("subtype");
159 Ok(serde_json::Value::Object(data))
160}