diff --git a/src/client.rs b/src/client.rs index 231f86b..f962125 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,11 +1,83 @@ + +use std::io::{self, BufRead, BufReader, BufWriter, LineWriter, Write}; +use std::net::TcpStream; +use std::path::{Path, PathBuf}; +use std::os::unix::net::UnixStream; + +use dirs; +use serde::Deserialize; + use crate::cli; +use crate::sand::message::{AddTimerResponse, Command}; + +fn get_sock_path() -> Option { + if let Ok(path) = std::env::var("SAND_SOCK_PATH") { + Some(path.into()) + } else { + Some(dirs::runtime_dir()?.join("sand.sock")) + } +} + +fn parse_durations(strs: &[String]) -> u64 { + // todo + 2000 +} + +struct DaemonConnection { + read: BufReader, + write: LineWriter, +} + +impl DaemonConnection { + fn new(sock_path: PathBuf) -> io::Result { + let stream = UnixStream::connect(sock_path)?; + + let read = BufReader::new(stream.try_clone()?); + let write = LineWriter::new(stream); + + Ok(Self { read, write }) + } + + fn send(&mut self, cmd: Command) -> io::Result<()> { + let str = serde_json::to_string(&cmd).expect("failed to serialize Command {cmd}"); + writeln!(self.write, "{str}") + } + + fn recv Deserialize<'de>>(&mut self) -> io::Result { + let mut recv_buf = String::with_capacity(128); + self.read.read_line(&mut recv_buf)?; + let resp: T = serde_json::from_str(&recv_buf).expect( + "Bug: failed to deserialize response from daemon" + ); + Ok(resp) + } +} + +pub fn main(cmd: cli::CliCommand) -> io::Result<()> { + let Some(sock_path) = get_sock_path() else { + eprintln!("socket not provided and runtime directory does not exist."); + eprintln!("no socket to use."); + std::process::exit(1) + }; + + let mut conn = match DaemonConnection::new(sock_path) { + Ok(conn) => conn, + Err(e) => { + eprintln!("Error establishing connection with daemon: {e}"); + std::process::exit(1); + }, + }; -pub fn main(cmd: cli::CliCommand) { match cmd { cli::CliCommand::Start { duration } => { - let duration_str = duration.join(" "); - println!("Starting new timer for duration: {}", duration_str); - todo!(); + let duration = parse_durations(&duration); + conn.send(Command::AddTimer { duration: duration })?; + let AddTimerResponse::Ok { id } = conn.recv::()?; + // TODO format duration + println!( + "Timer {id} created for {duration}." + ); + Ok(()) } cli::CliCommand::Ls => { println!("Listing timers..."); diff --git a/src/daemon/handle_client.rs b/src/daemon/handle_client.rs index 9964468..d92069d 100644 --- a/src/daemon/handle_client.rs +++ b/src/daemon/handle_client.rs @@ -94,7 +94,8 @@ pub async fn handle_client(mut stream: UnixStream, state: DaemonCtx) { Response::Error(err_msg) } }; - let resp_str: String = serde_json::to_string(&resp).unwrap(); + let mut resp_str: String = serde_json::to_string(&resp).unwrap(); + resp_str.push('\n'); write_half.write_all(resp_str.as_bytes()).await.unwrap(); } diff --git a/src/main.rs b/src/main.rs index 58117bf..adcaff4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,8 +18,7 @@ fn main() -> io::Result<()> { } CliCommand::Daemon(args) => daemon::main(args), _ => { - client::main(cli.command); - Ok(()) + client::main(cli.command) } } }