diff options
author | boasting-squirrel <boasting.squirrel@gmail.com> | 2019-02-14 21:24:07 +0000 |
---|---|---|
committer | boasting-squirrel <boasting.squirrel@gmail.com> | 2019-02-14 21:24:07 +0000 |
commit | fb63c8de43c538bcaf9afd57122fbf1c542966d0 (patch) | |
tree | ae711b4f0e8423ec3e1745fb0b3aa0e1c7998326 /src | |
parent | Merge pull request #32 from boastful-squirrel/split-files (diff) | |
download | miniserve-fb63c8de43c538bcaf9afd57122fbf1c542966d0.tar.gz miniserve-fb63c8de43c538bcaf9afd57122fbf1c542966d0.zip |
Switched to structopt
Diffstat (limited to 'src')
-rw-r--r-- | src/args.rs | 201 | ||||
-rw-r--r-- | src/listing.rs | 38 | ||||
-rw-r--r-- | src/main.rs | 8 |
3 files changed, 82 insertions, 165 deletions
diff --git a/src/args.rs b/src/args.rs index ae86108..5241b04 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,173 +1,106 @@ -use crate::auth; -use crate::listing; -use clap::{crate_authors, crate_description, crate_name, crate_version}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::path::PathBuf; +use structopt::StructOpt; + +use crate::auth; +use crate::listing; /// Possible characters for random routes const ROUTE_ALPHABET: [char; 16] = [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', ]; -/// Checks wether a path is valid, i.e. it exists on the system and points to a file/directory -fn is_valid_path(path: String) -> Result<(), String> { - let path_to_check = PathBuf::from(path); - if path_to_check.is_file() || path_to_check.is_dir() { - return Ok(()); - } - Err(String::from( - "Path either doesn't exist or is not a regular file or a directory", - )) -} - -/// Checks wether a port is valid -fn is_valid_port(port: String) -> Result<(), String> { - port.parse::<u16>() - .and(Ok(())) - .or_else(|e| Err(e.to_string())) +#[derive(StructOpt, Debug)] +#[structopt(name = "miniserve")] +struct CLIArgs { + /// Be verbose, includes emitting access logs + #[structopt(short = "v", long = "verbose")] + verbose: bool, + /// Which path to serve + #[structopt(name = "PATH", parse(from_os_str))] + path: Option<PathBuf>, + /// Port to use + #[structopt(short = "p", long = "port", default_value = "8080")] + port: u16, + /// Interface to listen on + #[structopt(short = "i", long = "if", parse(try_from_str = "parse_interface"))] + interfaces: Vec<IpAddr>, + /// Set authentication (username:password) + #[structopt(short = "a", long = "auth", parse(try_from_str = "parse_auth"))] + auth: Option<(String, String)>, + /// Generate a random 6-hexdigit route + #[structopt(long = "random-route")] + random_route: bool, + /// Sort files + #[structopt( + short = "s", + long = "sort", + raw( + possible_values = "&listing::SortingMethods::variants()", + case_insensitive = "true" + ) + )] + sort_method: Option<listing::SortingMethods>, + /// Reverse sorting + #[structopt(long = "reverse")] + reverse_sort: bool, + /// Do not follow symbolic links + #[structopt(short = "P", long = "no-symlinks")] + no_symlinks: bool, } - /// Checks wether an interface is valid, i.e. it can be parsed into an IP address -fn is_valid_interface(interface: String) -> Result<(), String> { - interface - .parse::<IpAddr>() - .and(Ok(())) - .or_else(|e| Err(e.to_string())) +fn parse_interface(src: &str) -> Result<IpAddr, std::net::AddrParseError> { + src.parse::<IpAddr>() } /// Checks wether the auth string is valid, i.e. it follows the syntax username:password -fn is_valid_auth(auth: String) -> Result<(), String> { - auth.find(':') - .ok_or_else(|| "Correct format is username:password".to_owned()) - .map(|_| ()) +fn parse_auth(src: &str) -> Result<(String, String), String> { + match src.find(':') { + Some(_) => { + let split = src.split(':').collect::<Vec<_>>(); + Ok((split[0].to_owned(), split[1].to_owned())) + } + None => Err("Correct format is username:password".to_owned()), + } } /// Parses the command line arguments pub fn parse_args() -> crate::MiniserveConfig { - use clap::{App, AppSettings, Arg}; + let args = CLIArgs::from_args(); - let matches = App::new(crate_name!()) - .version(crate_version!()) - .author(crate_authors!()) - .about(crate_description!()) - .global_setting(AppSettings::ColoredHelp) - .arg( - Arg::with_name("verbose") - .short("v") - .long("verbose") - .help("Be verbose, includes emitting access logs"), - ) - .arg( - Arg::with_name("PATH") - .required(false) - .validator(is_valid_path) - .help("Which path to serve"), - ) - .arg( - Arg::with_name("port") - .short("p") - .long("port") - .help("Port to use") - .validator(is_valid_port) - .required(false) - .default_value("8080") - .takes_value(true), - ) - .arg( - Arg::with_name("interfaces") - .short("i") - .long("if") - .help("Interface to listen on") - .validator(is_valid_interface) - .required(false) - .takes_value(true) - .multiple(true), - ) - .arg( - Arg::with_name("auth") - .short("a") - .long("auth") - .validator(is_valid_auth) - .help("Set authentication (username:password)") - .takes_value(true), - ) - .arg( - Arg::with_name("random-route") - .long("random-route") - .help("Generate a random 6-hexdigit route"), - ) - .arg( - Arg::with_name("sort") - .short("s") - .long("sort") - .possible_values(&["natural", "alpha", "dirsfirst"]) - .default_value("natural") - .help("Sort files"), - ) - .arg( - Arg::with_name("reverse") - .long("reverse") - .help("Reverse sorting order"), - ) - .arg( - Arg::with_name("no-symlinks") - .short("P") - .long("no-symlinks") - .help("Do not follow symbolic links"), - ) - .get_matches(); - - let verbose = matches.is_present("verbose"); - let no_symlinks = matches.is_present("no-symlinks"); - let path = matches.value_of("PATH"); - let port = matches.value_of("port").unwrap().parse().unwrap(); - let interfaces = if let Some(interfaces) = matches.values_of("interfaces") { - interfaces.map(|x| x.parse().unwrap()).collect() + let interfaces = if !args.interfaces.is_empty() { + args.interfaces } else { vec![ IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), ] }; - let auth = if let Some(auth_split) = matches.value_of("auth").map(|x| x.splitn(2, ':')) { - let auth_vec = auth_split.collect::<Vec<&str>>(); - if auth_vec.len() == 2 { - Some(auth::BasicAuthParams { - username: auth_vec[0].to_owned(), - password: auth_vec[1].to_owned(), - }) - } else { - None - } - } else { - None + + let auth = match args.auth { + Some((username, password)) => Some(auth::BasicAuthParams { username, password }), + None => None, }; - let random_route = if matches.is_present("random-route") { + let random_route = if args.random_route { Some(nanoid::custom(6, &ROUTE_ALPHABET)) } else { None }; - let sort_method = matches - .value_of("sort") - .unwrap() - .parse::<listing::SortingMethods>() - .unwrap(); - - let reverse_sort = matches.is_present("reverse"); + let path_explicitly_chosen = args.path.is_some(); crate::MiniserveConfig { - verbose, - path: PathBuf::from(path.unwrap_or(".")), - port, + verbose: args.verbose, + path: args.path.unwrap_or_else(|| PathBuf::from(".")), + port: args.port, interfaces, auth, - path_explicitly_chosen: path.is_some(), - no_symlinks, + path_explicitly_chosen, + no_symlinks: args.no_symlinks, random_route, - sort_method, - reverse_sort, + sort_method: args.sort_method.unwrap_or(listing::SortingMethods::Natural), + reverse_sort: args.reverse_sort, } } diff --git a/src/listing.rs b/src/listing.rs index f0662ef..6d956ac 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -6,26 +6,19 @@ use std::cmp::Ordering; use std::fmt::Write as FmtWrite; use std::io; use std::path::Path; -use std::str::FromStr; -#[derive(Clone, Copy, Debug)] -/// Available sorting methods -pub enum SortingMethods { - /// Natural sorting method - /// 1 -> 2 -> 3 -> 11 - Natural, - - /// Pure alphabetical sorting method - /// 1 -> 11 -> 2 -> 3 - Alpha, - - /// Directories are listed first, alphabetical sorting is also applied - /// 1/ -> 2/ -> 3/ -> 11 -> 12 - DirsFirst, +arg_enum! { + #[derive(Clone, Copy, Debug)] + /// Available sorting methods + pub enum SortingMethods { + Natural, + Alpha, + DirsFirst, + } } #[derive(PartialEq)] -/// Possible entry types +/// Possible entry types enum EntryType { /// Entry is a directory Directory, @@ -75,19 +68,6 @@ impl Entry { } } -impl FromStr for SortingMethods { - type Err = (); - - fn from_str(s: &str) -> Result<SortingMethods, ()> { - match s { - "natural" => Ok(SortingMethods::Natural), - "alpha" => Ok(SortingMethods::Alpha), - "dirsfirst" => Ok(SortingMethods::DirsFirst), - _ => Err(()), - } - } -} - pub fn file_handler(req: &HttpRequest<crate::MiniserveConfig>) -> Result<fs::NamedFile> { let path = &req.state().path; Ok(fs::NamedFile::open(path)?) diff --git a/src/main.rs b/src/main.rs index 7c31976..7bdb986 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,7 @@ +extern crate structopt; +#[macro_use] +extern crate clap; + use actix_web::{fs, middleware, server, App}; use clap::crate_version; use simplelog::{Config, LevelFilter, TermLogger}; @@ -16,11 +20,11 @@ mod listing; pub struct MiniserveConfig { /// Enable verbose mode pub verbose: bool, - + /// Path to be served by miniserve pub path: std::path::PathBuf, - /// Port on which miniserve will be listening + /// Port on which miniserve will be listening pub port: u16, /// IP address(es) on which miniserve will be available |