diff options
-rw-r--r-- | src/client.rs | 50 | ||||
-rw-r--r-- | src/main.rs | 45 | ||||
-rw-r--r-- | src/server.rs | 6 |
3 files changed, 92 insertions, 9 deletions
diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..492008a --- /dev/null +++ b/src/client.rs @@ -0,0 +1,50 @@ +use anyhow::{anyhow, bail, Result}; +use std::{ + io::{BufRead, BufReader, BufWriter, Write}, + net::{TcpStream, ToSocketAddrs}, +}; + +use crate::Serial; + +pub struct Client { + wsock: BufWriter<TcpStream>, + rsock: BufReader<TcpStream>, +} +impl Client { + pub fn new(address: impl ToSocketAddrs, secret: &str) -> Result<Self> { + let mut sock = TcpStream::connect(address)?; + writeln!(sock, "{secret}")?; + Ok(Self { + rsock: BufReader::new(sock.try_clone()?), + wsock: BufWriter::new(sock), + }) + } + pub fn list(&mut self) -> Result<Vec<(i64, u64, Serial)>> { + writeln!(self.wsock, "list")?; + self.wsock.flush()?; + let mut line = String::new(); + let mut out = Vec::new(); + loop { + self.rsock.read_line(&mut line)?; + if line.trim().is_empty() || line.ends_with("\n\n") { + break Ok(out); + } + check_error(&line)?; + let (mtime, rest) = line.trim().split_once(":").ok_or(anyhow!("size missing"))?; + let (size, serial) = rest.split_once(":").ok_or(anyhow!("serial missing"))?; + out.push((mtime.parse()?, size.parse()?, serial.parse()?)); + } + } + pub fn quit(mut self) -> Result<()> { + writeln!(self.wsock, "quit")?; + self.wsock.flush()?; + Ok(()) + } +} + +fn check_error(line: &str) -> Result<()> { + if let Some(message) = line.trim().strip_prefix("error") { + bail!("server error: {message}") + } + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 243c621..c72aad7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ -#![feature(iterator_try_collect)] -#![feature(never_type)] +#![feature(iterator_try_collect, never_type)] +pub mod client; pub mod server; +use anyhow::Result; use clap::{Parser, Subcommand}; +use client::Client; use serde::Deserialize; use server::server; use std::{fs::read_to_string, net::SocketAddr, path::PathBuf}; @@ -19,8 +21,17 @@ struct Args { #[derive(Subcommand)] enum Action { Daemon, - Restore { id: String, destination: PathBuf }, - Backup { path: PathBuf }, + List { + peer: Option<String>, + }, + Restore { + peer: String, + id: String, + destination: PathBuf, + }, + Backup { + path: PathBuf, + }, } #[derive(Deserialize)] @@ -53,7 +64,7 @@ pub struct StorageConfig { download_speed: usize, } -fn main() -> anyhow::Result<()> { +fn main() -> Result<()> { env_logger::init_from_env("LOG"); let args = Args::parse(); @@ -62,7 +73,29 @@ fn main() -> anyhow::Result<()> { match args.action { Action::Daemon => server(config.into())?, - Action::Restore { id, destination } => todo!(), + Action::List { peer } => { + let peers = config.peer.iter().filter(|p| { + if let Some(pn) = &peer { + pn == &p.name + } else { + true + } + }); + + for p in peers { + println!("peer {:?}", p.name); + let mut client = Client::new(p.address, &p.shared_secret)?; + for (mtime, size, serial) in client.list()? { + println!("\tserial={serial} mtime={mtime} size={size}") + } + client.quit()?; + } + } + Action::Restore { + id, + destination, + peer, + } => todo!(), Action::Backup { path } => todo!(), } Ok(()) diff --git a/src/server.rs b/src/server.rs index 30ed0c8..d24f1b4 100644 --- a/src/server.rs +++ b/src/server.rs @@ -24,7 +24,9 @@ pub fn server(config: Arc<Config>) -> anyhow::Result<!> { let config = config.clone(); spawn( move || match handle_connection_wrapper(config, sock, addr) { - Ok(()) => (), + Ok(()) => { + info!("connection closed gracefully") + } Err(err) => { warn!("client error {addr}: {err:?}") } @@ -60,7 +62,6 @@ fn handle_connection( rsock.by_ref().take(4096).read_line(&mut line)?; let provided_secret = line.trim(); - eprintln!("{:?}", provided_secret); let Some(peer) = config .peer .iter() @@ -77,7 +78,6 @@ fn handle_connection( rsock.by_ref().take(4096).read_line(&mut line)?; let mut toks = line.trim().split(","); let command = toks.next().ok_or(anyhow!("command missing"))?; - eprintln!("{command:?}"); match command { "quit" => break Ok(()), "list" => { |