1use tokio::io::{ReadHalf, WriteHalf};
4
5use crate::common::{Error, Result};
6
7use super::protocol::{Command, Request, Response};
8use super::transport::{self, Stream};
9
10pub struct DaemonClient {
12 reader: ReadHalf<Stream>,
13 writer: WriteHalf<Stream>,
14 next_id: u64,
15}
16
17impl DaemonClient {
18 pub async fn connect() -> Result<Self> {
20 let stream = transport::connect().await.map_err(|e| {
21 if e.kind() == std::io::ErrorKind::NotFound
22 || e.kind() == std::io::ErrorKind::ConnectionRefused
23 {
24 Error::DaemonNotRunning
25 } else {
26 Error::DaemonConnectionFailed(e)
27 }
28 })?;
29
30 let (reader, writer) = tokio::io::split(stream);
31
32 Ok(Self {
33 reader,
34 writer,
35 next_id: 1,
36 })
37 }
38
39 pub async fn send_command(&mut self, command: Command) -> Result<serde_json::Value> {
41 let id = self.next_id;
42 self.next_id += 1;
43
44 let request = Request { id, command };
45 let json = serde_json::to_vec(&request)?;
46
47 transport::send_message(&mut self.writer, &json)
48 .await
49 .map_err(|e| Error::DaemonCommunication(e.to_string()))?;
50
51 let response_data = transport::recv_message(&mut self.reader)
52 .await
53 .map_err(|e| Error::DaemonCommunication(e.to_string()))?;
54
55 let response: Response = serde_json::from_slice(&response_data)?;
56
57 if response.id != id {
58 return Err(Error::DaemonCommunication(format!(
59 "Response ID mismatch: expected {}, got {}",
60 id, response.id
61 )));
62 }
63
64 if response.success {
65 Ok(response.result.unwrap_or(serde_json::json!({})))
66 } else {
67 let error = response
68 .error
69 .unwrap_or_else(|| crate::common::error::IpcError {
70 code: "UNKNOWN".to_string(),
71 message: "Unknown error".to_string(),
72 });
73 Err(error.into())
74 }
75 }
76
77 pub async fn ping(&mut self) -> Result<bool> {
79 match self.send_command(Command::Status).await {
80 Ok(_) => Ok(true),
81 Err(Error::DaemonNotRunning) => Ok(false),
82 Err(e) => Err(e),
83 }
84 }
85}