use std::{collections::HashMap, os::unix::net::UnixStream, path::PathBuf}; use keyfork_frame::{try_decode_from, try_encode_to, DecodeError, EncodeError}; use keyforkd_models::{Request, Response /* Error as KeyforkdError */}; #[cfg(test)] mod tests; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Neither KEYFORK_SOCKET_PATH nor XDG_RUNTIME_DIR were set")] EnvVarsNotFound, #[error("Socket was unable to connect to {1}: {0}")] Connect(std::io::Error, PathBuf), #[error("Could not write to or from the socket: {0}")] Io(#[from] std::io::Error), #[error("Could not perform bincode transformation: {0}")] Bincode(#[from] Box), #[error("Could not perform frame transformation: {0}")] FrameEnc(#[from] EncodeError), #[error("Could not perform frame transformation: {0}")] FrameDec(#[from] DecodeError), } pub type Result = std::result::Result; pub fn get_socket() -> Result { let socket_vars = std::env::vars() .filter(|(key, _)| ["XDG_RUNTIME_DIR", "KEYFORKD_SOCKET_PATH"].contains(&key.as_str())) .collect::>(); let mut socket_path: PathBuf; #[allow(clippy::single_match_else)] match socket_vars.get("KEYFORKD_SOCKET_PATH") { Some(occupied) => { socket_path = PathBuf::from(occupied); } None => { socket_path = PathBuf::from( socket_vars .get("XDG_RUNTIME_DIR") .ok_or(Error::EnvVarsNotFound)?, ); socket_path.extend(["keyforkd", "keyforkd.sock"]); } } UnixStream::connect(&socket_path).map_err(|e| Error::Connect(e, socket_path)) } #[derive(Debug)] pub struct Client { socket: UnixStream, } impl Client { pub fn new(socket: UnixStream) -> Self { Self { socket } } pub fn discover_socket() -> Result { get_socket().map(|socket| Self { socket }) } pub fn request(&mut self, req: &Request) -> Result { try_encode_to(&bincode::serialize(&req)?, &mut self.socket)?; let resp = try_decode_from(&mut self.socket)?; bincode::deserialize(&resp).map_err(From::from) } }