From 213dd3124f0d3df663d6e7524343a1ce310503d8 Mon Sep 17 00:00:00 2001 From: 17ms <79069176+17ms@users.noreply.github.com> Date: Sat, 6 May 2023 20:36:46 +0300 Subject: [PATCH] clap parsers for ips & paths --- src/lib.rs | 2 ++ src/main.rs | 23 +++++++------- src/parsers.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/ui.rs | 17 +++++++++++ 4 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 src/parsers.rs create mode 100644 src/ui.rs diff --git a/src/lib.rs b/src/lib.rs index d6d8481..68ad603 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,3 +3,5 @@ pub mod comms; pub mod connector; pub mod crypto; pub mod listener; +pub mod parsers; +pub mod ui; diff --git a/src/main.rs b/src/main.rs index 1f91b34..8e13dbc 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use clap::{Parser, Subcommand}; -use std::error::Error; +use contego::parsers::{addr_parser, dirpath_parser, filepath_parser}; +use std::{error::Error, net::SocketAddr, path::PathBuf}; #[derive(Debug, Parser)] #[command(about, version)] @@ -11,8 +12,6 @@ struct Cli { quiet: bool, } -// TODO: add validators for infile, outdir & address (IPv4/6) - #[derive(Debug, Subcommand)] enum Commands { /// Host fileserver instance by providing JSON file with paths or list of paths @@ -20,19 +19,21 @@ enum Commands { /// Use IPv6 instead of IPv4 #[clap(short = '6', long, default_value_t = false)] ipv6: bool, - /// Path to the inputfile (JSON) - infile: Option, - /// Paths to the files - #[clap(short = 'f', long)] - files: Option>, + /// Path to the inputfile + #[clap(short = 'i', long, value_parser = filepath_parser)] + infile: Option, + /// Paths to the files (comma separated) + #[clap(short = 'f', long, num_args = 1.., value_parser = filepath_parser)] + files: Option>, }, /// Connect to hosted server by providing address, output folder and access key Connect { /// IP address of the server (IPv4 or IPv6) - address: String, + #[clap(short = 'a', long, value_parser = addr_parser)] + address: SocketAddr, /// Path to the output folder - #[clap(short = 'o', long)] - outdir: String, + #[clap(short = 'o', long, value_parser = dirpath_parser)] + outdir: PathBuf, /// Access key for the fileserver #[clap(short = 'k', long)] key: String, diff --git a/src/parsers.rs b/src/parsers.rs new file mode 100644 index 0000000..998a69e --- /dev/null +++ b/src/parsers.rs @@ -0,0 +1,83 @@ +use std::{ + io::{Error, ErrorKind::NotFound}, + net::{AddrParseError, SocketAddr}, + path::PathBuf, +}; + +pub fn addr_parser(addr: &str) -> Result { + let addr = addr + .parse::() + .expect("Failed to parse IP address"); + + Ok(addr) +} + +pub fn filepath_parser(path: &str) -> Result { + let path = path.parse::().expect("Failed to parse path"); + + if path.exists() && path.is_file() { + Ok(path) + } else { + Err(Error::new(NotFound, "File not found")) + } +} + +pub fn dirpath_parser(path: &str) -> Result { + let path = path.parse::().expect("Failed to parse path"); + + if path.exists() && path.is_dir() { + Ok(path) + } else { + Err(Error::new(NotFound, "Directory not found")) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn valid_ip() { + use std::net::Ipv6Addr; + + let ipv4 = "10.1.2.3:8888"; + let ipv6 = "[2001:db8::1]:8888"; + + let parsed_ipv4 = addr_parser(ipv4).unwrap(); + let parsed_ipv6 = addr_parser(ipv6).unwrap(); + + assert_eq!(parsed_ipv4, SocketAddr::from(([10, 1, 2, 3], 8888))); + assert_eq!( + parsed_ipv6, + SocketAddr::from((Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8888)) + ); + } + + #[test] + #[should_panic] + fn short_ip() { + let ip = "10.1.2:8888"; + addr_parser(ip).unwrap(); + } + + #[test] + #[should_panic] + fn long_ip() { + let ip = "[2001:0db8:ac10:fe01:0000:0000:0000:0000:0000]:8888"; + addr_parser(ip).unwrap(); + } + + #[test] + #[should_panic] + fn ipv6_no_brackets() { + let ip = "2001:db8::1:8888"; + addr_parser(ip).unwrap(); + } + + #[test] + #[should_panic] + fn ip_missing_port() { + let ip = "10.1.2.3"; + addr_parser(ip).unwrap(); + } +} diff --git a/src/ui.rs b/src/ui.rs new file mode 100644 index 0000000..83e9ea4 --- /dev/null +++ b/src/ui.rs @@ -0,0 +1,17 @@ +/* +TODO: + +- suspend everything except errors and critical exceptions that lead to panic or abort + +server: +- ip & access key on display +- commands to copy ip/key to clipboard +- updating list/log of connecting/disconnecting clients (?) + +client: +- connection ip on display +- comamnds to download one or more files +- updating list of available/downloading/downloaded files (?) + +* (?): maybe requires a separate UI thread for updates +*/