1#![allow(dead_code)]
4
5use std::path::Path;
6
7use anyhow::{Context, Result};
8use tokio::io::{ReadHalf, WriteHalf};
9
10use super::protocol::{Command, Request, Response};
11use super::transport::{self, Stream};
12
13pub struct DaemonClient {
15 reader: ReadHalf<Stream>,
16 writer: WriteHalf<Stream>,
17 next_id: u64,
18}
19
20impl DaemonClient {
21 pub async fn connect(project_path: &Path) -> Result<Self> {
23 let stream = transport::connect_for_project(project_path)
24 .await
25 .map_err(|e| {
26 if e.kind() == std::io::ErrorKind::NotFound
27 || e.kind() == std::io::ErrorKind::ConnectionRefused
28 {
29 anyhow::anyhow!("Daemon not running for project: {}", project_path.display())
30 } else {
31 anyhow::anyhow!("Failed to connect to daemon: {}", e)
32 }
33 })?;
34
35 let (reader, writer) = tokio::io::split(stream);
36
37 Ok(Self {
38 reader,
39 writer,
40 next_id: 1,
41 })
42 }
43
44 pub async fn send_command(&mut self, command: Command) -> Result<serde_json::Value> {
46 let id = self.next_id;
47 self.next_id += 1;
48
49 let request = Request::new(id, command);
50 let json = serde_json::to_vec(&request).context("Failed to serialize request")?;
51
52 transport::send_message(&mut self.writer, &json)
53 .await
54 .context("Failed to send message to daemon")?;
55
56 let response_data = transport::recv_message(&mut self.reader)
57 .await
58 .context("Failed to receive message from daemon")?;
59
60 let response: Response =
61 serde_json::from_slice(&response_data).context("Failed to parse daemon response")?;
62
63 if response.id != id {
64 anyhow::bail!("Response ID mismatch: expected {}, got {}", id, response.id);
65 }
66
67 if response.success {
68 Ok(response.result.unwrap_or(serde_json::json!({})))
69 } else {
70 let error = response
71 .error
72 .unwrap_or_else(|| "Unknown error".to_string());
73 anyhow::bail!("{}", error)
74 }
75 }
76
77 pub async fn ping(&mut self) -> Result<bool> {
79 match self.send_command(Command::Ping).await {
80 Ok(_) => Ok(true),
81 Err(e) if e.to_string().contains("not running") => Ok(false),
82 Err(e) => Err(e),
83 }
84 }
85
86 pub async fn status(&mut self) -> Result<serde_json::Value> {
88 self.send_command(Command::Status).await
89 }
90
91 pub async fn shutdown(&mut self) -> Result<()> {
93 self.send_command(Command::Shutdown).await?;
94 Ok(())
95 }
96
97 pub async fn clear_cache(&mut self) -> Result<()> {
99 self.send_command(Command::ClearCache).await?;
100 Ok(())
101 }
102
103 pub async fn list_functions(
105 &mut self,
106 limit: Option<usize>,
107 filter: Option<String>,
108 ) -> Result<serde_json::Value> {
109 self.send_command(Command::ListFunctions { limit, filter })
110 .await
111 }
112
113 pub async fn decompile(&mut self, address: String) -> Result<serde_json::Value> {
115 self.send_command(Command::Decompile { address }).await
116 }
117
118 pub async fn list_strings(&mut self, limit: Option<usize>) -> Result<serde_json::Value> {
120 self.send_command(Command::ListStrings { limit }).await
121 }
122
123 pub async fn list_imports(&mut self) -> Result<serde_json::Value> {
125 self.send_command(Command::ListImports).await
126 }
127
128 pub async fn list_exports(&mut self) -> Result<serde_json::Value> {
130 self.send_command(Command::ListExports).await
131 }
132
133 pub async fn memory_map(&mut self) -> Result<serde_json::Value> {
135 self.send_command(Command::MemoryMap).await
136 }
137
138 pub async fn program_info(&mut self) -> Result<serde_json::Value> {
140 self.send_command(Command::ProgramInfo).await
141 }
142
143 pub async fn xrefs_to(&mut self, address: String) -> Result<serde_json::Value> {
145 self.send_command(Command::XRefsTo { address }).await
146 }
147
148 pub async fn xrefs_from(&mut self, address: String) -> Result<serde_json::Value> {
150 self.send_command(Command::XRefsFrom { address }).await
151 }
152
153 pub async fn execute_cli_json(&mut self, command_json: String) -> Result<serde_json::Value> {
155 self.send_command(Command::ExecuteCli { command_json })
156 .await
157 }
158
159 pub async fn import_binary(
161 &mut self,
162 binary_path: &str,
163 project: &str,
164 program: Option<&str>,
165 ) -> Result<serde_json::Value> {
166 self.send_command(Command::Import {
167 binary_path: binary_path.to_string(),
168 project: project.to_string(),
169 program: program.map(|s| s.to_string()),
170 })
171 .await
172 }
173
174 pub async fn analyze_program(
176 &mut self,
177 project: &str,
178 program: &str,
179 ) -> Result<serde_json::Value> {
180 self.send_command(Command::Analyze {
181 project: project.to_string(),
182 program: program.to_string(),
183 })
184 .await
185 }
186}
187
188pub fn daemon_available(project_path: &Path) -> bool {
190 transport::socket_exists_for_project(project_path)
191}