claude_codes/client/
sync.rs1use crate::cli::ClaudeCliBuilder;
4use crate::error::{Error, Result};
5use crate::io::{ClaudeInput, ClaudeOutput, ParseError};
6use crate::protocol::Protocol;
7use std::io::{BufRead, BufReader};
8use std::process::{Child, ChildStdin, ChildStdout};
9use tracing::debug;
10
11pub struct SyncClient {
13 child: Child,
14 stdin: ChildStdin,
15 stdout: BufReader<ChildStdout>,
16}
17
18impl SyncClient {
19 pub fn new(mut child: Child) -> Result<Self> {
21 let stdin = child
22 .stdin
23 .take()
24 .ok_or_else(|| Error::Protocol("Failed to get stdin".to_string()))?;
25 let stdout = child
26 .stdout
27 .take()
28 .ok_or_else(|| Error::Protocol("Failed to get stdout".to_string()))?;
29
30 Ok(Self {
31 child,
32 stdin,
33 stdout: BufReader::new(stdout),
34 })
35 }
36
37 pub fn with_defaults() -> Result<Self> {
39 crate::version::check_claude_version()?;
45 let child = ClaudeCliBuilder::new().spawn_sync().map_err(Error::Io)?;
46 Self::new(child)
47 }
48
49 pub fn query(&mut self, input: ClaudeInput) -> Result<Vec<ClaudeOutput>> {
51 let mut responses = Vec::new();
52 for response in self.query_stream(input)? {
53 responses.push(response?);
54 }
55 Ok(responses)
56 }
57
58 pub fn query_stream(&mut self, input: ClaudeInput) -> Result<ResponseIterator<'_>> {
60 Protocol::write_sync(&mut self.stdin, &input)?;
62
63 Ok(ResponseIterator {
64 client: self,
65 finished: false,
66 })
67 }
68
69 fn read_next(&mut self) -> Result<Option<ClaudeOutput>> {
71 let mut line = String::new();
72 match self.stdout.read_line(&mut line) {
73 Ok(0) => {
74 debug!("[CLIENT] Stream closed");
75 Ok(None)
76 }
77 Ok(_) => {
78 let trimmed = line.trim();
79 if trimmed.is_empty() {
80 debug!("[CLIENT] Skipping empty line");
81 return self.read_next();
82 }
83
84 debug!("[CLIENT] Received: {}", trimmed);
85 match ClaudeOutput::parse_json(trimmed) {
86 Ok(output) => {
87 if matches!(output, ClaudeOutput::Result(_)) {
89 debug!("[CLIENT] Received result message, stream complete");
90 Ok(Some(output))
91 } else {
92 Ok(Some(output))
93 }
94 }
95 Err(ParseError { error_message, .. }) => {
96 debug!("[CLIENT] Failed to deserialize: {}", error_message);
97 Err(Error::Deserialization(error_message))
98 }
99 }
100 }
101 Err(e) => {
102 debug!("[CLIENT] Error reading from stdout: {}", e);
103 Err(Error::Io(e))
104 }
105 }
106 }
107
108 pub fn shutdown(&mut self) -> Result<()> {
110 debug!("[CLIENT] Shutting down client");
111 self.child.kill().map_err(Error::Io)?;
112 self.child.wait().map_err(Error::Io)?;
113 Ok(())
114 }
115}
116
117pub struct ResponseIterator<'a> {
119 client: &'a mut SyncClient,
120 finished: bool,
121}
122
123impl Iterator for ResponseIterator<'_> {
124 type Item = Result<ClaudeOutput>;
125
126 fn next(&mut self) -> Option<Self::Item> {
127 if self.finished {
128 return None;
129 }
130
131 match self.client.read_next() {
132 Ok(Some(output)) => {
133 if matches!(output, ClaudeOutput::Result(_)) {
135 self.finished = true;
136 }
137 Some(Ok(output))
138 }
139 Ok(None) => {
140 self.finished = true;
141 None
142 }
143 Err(e) => {
144 self.finished = true;
145 Some(Err(e))
146 }
147 }
148 }
149}
150
151impl Drop for SyncClient {
152 fn drop(&mut self) {
153 if let Err(e) = self.shutdown() {
154 debug!("[CLIENT] Error during shutdown: {}", e);
155 }
156 }
157}