2023-09-28 02:30:56 +00:00
|
|
|
use std::{collections::HashMap, os::unix::net::UnixStream, path::PathBuf};
|
|
|
|
|
|
|
|
use keyfork_frame::{try_decode_from, try_encode_to, DecodeError, EncodeError};
|
2023-11-06 05:15:48 +00:00
|
|
|
use keyforkd_models::{Request, Response, Error as KeyforkdError};
|
2023-09-28 02:30:56 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
pub enum Error {
|
|
|
|
#[error("Neither KEYFORK_SOCKET_PATH nor XDG_RUNTIME_DIR were set")]
|
|
|
|
EnvVarsNotFound,
|
|
|
|
|
2024-01-08 19:06:03 +00:00
|
|
|
#[error("Socket was unable to connect to {1}: {0} (make sure keyforkd is running)")]
|
2023-09-28 02:30:56 +00:00
|
|
|
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<bincode::ErrorKind>),
|
|
|
|
|
|
|
|
#[error("Could not perform frame transformation: {0}")]
|
|
|
|
FrameEnc(#[from] EncodeError),
|
|
|
|
|
|
|
|
#[error("Could not perform frame transformation: {0}")]
|
|
|
|
FrameDec(#[from] DecodeError),
|
2023-11-06 05:15:48 +00:00
|
|
|
|
|
|
|
#[error("Error in Keyforkd: {0}")]
|
|
|
|
Keyforkd(#[from] KeyforkdError)
|
2023-09-28 02:30:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
|
|
|
|
|
|
|
pub fn get_socket() -> Result<UnixStream, Error> {
|
|
|
|
let socket_vars = std::env::vars()
|
|
|
|
.filter(|(key, _)| ["XDG_RUNTIME_DIR", "KEYFORKD_SOCKET_PATH"].contains(&key.as_str()))
|
|
|
|
.collect::<HashMap<String, String>>();
|
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2023-11-06 05:15:48 +00:00
|
|
|
/// A client to interact with Keyforkd.
|
|
|
|
///
|
|
|
|
/// Upon creation, a socket is opened, and is kept open for the duration of the object's lifetime.
|
|
|
|
/// Currently, Keyforkd does not support the reuse of sockets. Attempting to reuse the socket after
|
|
|
|
/// previously using it will likely result in an error.
|
2023-09-28 02:30:56 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Client {
|
|
|
|
socket: UnixStream,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Client {
|
2023-11-06 05:15:48 +00:00
|
|
|
/// Create a new client from a given already-connected [`UnixStream`].
|
2023-09-28 02:30:56 +00:00
|
|
|
pub fn new(socket: UnixStream) -> Self {
|
|
|
|
Self { socket }
|
|
|
|
}
|
|
|
|
|
2023-11-06 05:15:48 +00:00
|
|
|
/// Create a new client using well-known socket locations.
|
2023-09-28 02:30:56 +00:00
|
|
|
pub fn discover_socket() -> Result<Self> {
|
|
|
|
get_socket().map(|socket| Self { socket })
|
|
|
|
}
|
|
|
|
|
2023-11-06 05:15:48 +00:00
|
|
|
/// Serialize and send a [`Request`] to the server, awaiting a [`Result<Response>`].
|
2023-11-06 04:51:40 +00:00
|
|
|
pub fn request(&mut self, req: &Request) -> Result<Response> {
|
2023-09-28 02:30:56 +00:00
|
|
|
try_encode_to(&bincode::serialize(&req)?, &mut self.socket)?;
|
|
|
|
let resp = try_decode_from(&mut self.socket)?;
|
2023-11-06 05:15:48 +00:00
|
|
|
let resp: Result<Response, KeyforkdError> = bincode::deserialize(&resp)?;
|
|
|
|
resp.map_err(From::from)
|
2023-09-28 02:30:56 +00:00
|
|
|
}
|
|
|
|
}
|