use std::io::{Read, Write};
use std::os::unix::net::UnixStream;
use std::path::Path;
use std::time::Duration;
use crate::vortix_core::ipc::{
decode_frame, encode_frame, FrameError, IpcError, IpcOp, IpcRequest, IpcResponse, IpcResult,
};
#[derive(Debug)]
pub enum ClientError {
Io(std::io::Error),
Frame(FrameError),
Daemon(IpcError),
Unexpected(String),
}
impl std::fmt::Display for ClientError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io(e) => write!(f, "ipc io: {e}"),
Self::Frame(e) => write!(f, "ipc frame: {e}"),
Self::Daemon(e) => write!(f, "daemon error: {e}"),
Self::Unexpected(s) => write!(f, "unexpected daemon response: {s}"),
}
}
}
impl std::error::Error for ClientError {}
impl From<std::io::Error> for ClientError {
fn from(e: std::io::Error) -> Self {
Self::Io(e)
}
}
impl From<FrameError> for ClientError {
fn from(e: FrameError) -> Self {
Self::Frame(e)
}
}
pub fn request(socket_path: &Path, op: IpcOp) -> Result<IpcResult, ClientError> {
let mut stream = UnixStream::connect(socket_path)?;
stream.set_read_timeout(Some(Duration::from_secs(2)))?;
stream.set_write_timeout(Some(Duration::from_secs(2)))?;
let req = IpcRequest { id: 1, op };
let frame = encode_frame(&req)?;
stream.write_all(&frame)?;
let mut buf = Vec::with_capacity(4096);
let mut chunk = [0u8; 4096];
let resp: IpcResponse = loop {
if let Some((resp, _consumed)) = decode_frame::<IpcResponse>(&buf)? {
break resp;
}
let n = stream.read(&mut chunk)?;
if n == 0 {
return Err(ClientError::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"daemon closed connection without responding",
)));
}
buf.extend_from_slice(&chunk[..n]);
};
resp.result.map_err(ClientError::Daemon)
}
pub fn snapshot(
socket_path: &Path,
) -> Result<crate::vortix_core::engine::state::Connection, ClientError> {
match request(socket_path, IpcOp::Snapshot)? {
IpcResult::Snapshot { state } => Ok(state),
other => Err(ClientError::Unexpected(format!("{other:?}"))),
}
}