clap parsers for ips & paths

This commit is contained in:
17ms 2023-05-06 20:36:46 +03:00
parent 63de6d49cc
commit 213dd3124f
4 changed files with 114 additions and 11 deletions

View File

@ -3,3 +3,5 @@ pub mod comms;
pub mod connector;
pub mod crypto;
pub mod listener;
pub mod parsers;
pub mod ui;

View File

@ -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<String>,
/// Paths to the files
#[clap(short = 'f', long)]
files: Option<Vec<String>>,
/// Path to the inputfile
#[clap(short = 'i', long, value_parser = filepath_parser)]
infile: Option<PathBuf>,
/// Paths to the files (comma separated)
#[clap(short = 'f', long, num_args = 1.., value_parser = filepath_parser)]
files: Option<Vec<PathBuf>>,
},
/// 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,

83
src/parsers.rs Normal file
View File

@ -0,0 +1,83 @@
use std::{
io::{Error, ErrorKind::NotFound},
net::{AddrParseError, SocketAddr},
path::PathBuf,
};
pub fn addr_parser(addr: &str) -> Result<SocketAddr, AddrParseError> {
let addr = addr
.parse::<SocketAddr>()
.expect("Failed to parse IP address");
Ok(addr)
}
pub fn filepath_parser(path: &str) -> Result<PathBuf, Error> {
let path = path.parse::<PathBuf>().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<PathBuf, Error> {
let path = path.parse::<PathBuf>().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();
}
}

17
src/ui.rs Normal file
View File

@ -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
*/