Skip to main content

claude_code_sdk_rust/internal/
parser.rs

1use 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
45// Surface the offending payload alongside the serde error; a bare
46// "invalid type: sequence, expected a map" is undebuggable without it.
47fn 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}