syspulse_core/ipc/
client.rs1use std::path::PathBuf;
2
3use tracing::debug;
4
5use crate::error::{Result, SyspulseError};
6use crate::ipc::protocol::{read_message, write_message, Request, Response};
7
8#[cfg(unix)]
9use interprocess::local_socket::GenericFilePath as NameType;
10#[cfg(windows)]
11use interprocess::local_socket::GenericNamespaced as NameType;
12
13use interprocess::local_socket::tokio::prelude::*;
14
15pub struct IpcClient {
16 socket_path: PathBuf,
17}
18
19impl IpcClient {
20 pub fn new(socket_path: PathBuf) -> Self {
21 Self { socket_path }
22 }
23
24 pub async fn send(&self, request: Request) -> Result<Response> {
27 let name = self.socket_name()?;
28 let stream = LocalSocketStream::connect(name)
29 .await
30 .map_err(|e| SyspulseError::Ipc(format!("Failed to connect: {}", e)))?;
31
32 let (mut reader, mut writer) = tokio::io::split(stream);
33
34 write_message(&mut writer, &request).await?;
35 debug!("Sent IPC request: {:?}", request);
36
37 let response: Response = read_message(&mut reader).await?.ok_or_else(|| {
38 SyspulseError::Ipc("Server closed connection without response".into())
39 })?;
40
41 debug!("Received IPC response: {:?}", response);
42 Ok(response)
43 }
44
45 pub async fn is_manager_running(&self) -> bool {
47 match self.send(Request::Ping).await {
48 Ok(Response::Pong) => true,
49 _ => false,
50 }
51 }
52
53 fn socket_name(&self) -> Result<interprocess::local_socket::Name<'_>> {
54 #[cfg(unix)]
55 {
56 let path_str = self
57 .socket_path
58 .to_str()
59 .ok_or_else(|| SyspulseError::Ipc("Invalid socket path".into()))?;
60 path_str
61 .to_ns_name::<NameType>()
62 .map_err(|e| SyspulseError::Ipc(format!("Invalid socket name: {}", e)))
63 }
64 #[cfg(windows)]
65 {
66 let name_str = self
67 .socket_path
68 .file_name()
69 .and_then(|n| n.to_str())
70 .unwrap_or("syspulse");
71 name_str
72 .to_ns_name::<NameType>()
73 .map_err(|e| SyspulseError::Ipc(format!("Invalid pipe name: {}", e)))
74 }
75 }
76}