aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven-Hendrik Haase <svenstaro@gmail.com>2019-02-16 20:40:19 +0000
committerGitHub <noreply@github.com>2019-02-16 20:40:19 +0000
commit9c4e46fa570e6e4498f3d9643cc4627705c53e5a (patch)
treecf4efcc073766edb3b1bb6529be4c3f0ae8ada83 /src
parentAdd rsync to alpine image to allow for syncing files into it (diff)
parentUpdated README (diff)
downloadminiserve-9c4e46fa570e6e4498f3d9643cc4627705c53e5a.tar.gz
miniserve-9c4e46fa570e6e4498f3d9643cc4627705c53e5a.zip
Merge pull request #35 from boastful-squirrel/structopt
Switched to structopt
Diffstat (limited to 'src')
-rw-r--r--src/args.rs217
-rw-r--r--src/listing.rs42
-rw-r--r--src/main.rs4
3 files changed, 101 insertions, 162 deletions
diff --git a/src/args.rs b/src/args.rs
index ae86108..637c224 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -1,173 +1,122 @@
-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",
- ))
-}
+#[derive(StructOpt, Debug)]
+#[structopt(
+ name = "miniserve",
+ raw(global_settings = "&[structopt::clap::AppSettings::ColoredHelp]")
+)]
+struct CLIArgs {
+ /// Be verbose, includes emitting access logs
+ #[structopt(short = "v", long = "verbose")]
+ verbose: bool,
-/// 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()))
-}
+ /// 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"),
+ raw(number_of_values = "1")
+ )]
+ 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 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 args = CLIArgs::from_args();
+
+ 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..056c847 100644
--- a/src/listing.rs
+++ b/src/listing.rs
@@ -1,31 +1,34 @@
use actix_web::{fs, HttpRequest, HttpResponse, Result};
use bytesize::ByteSize;
+use clap::{_clap_count_exprs, arg_enum};
use htmlescape::encode_minimal as escape_html_entity;
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
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
+arg_enum! {
+ #[derive(Clone, Copy, Debug)]
+ /// Available sorting methods
+ ///
+ /// Natural: natural sorting method
/// 1 -> 2 -> 3 -> 11
- Natural,
-
- /// Pure alphabetical sorting method
+ ///
+ /// Alpha: pure alphabetical sorting method
/// 1 -> 11 -> 2 -> 3
- Alpha,
-
- /// Directories are listed first, alphabetical sorting is also applied
+ ///
+ /// DirsFirst: directories are listed first, alphabetical sorting is also applied
/// 1/ -> 2/ -> 3/ -> 11 -> 12
- DirsFirst,
+ pub enum SortingMethods {
+ Natural,
+ Alpha,
+ DirsFirst,
+ }
}
#[derive(PartialEq)]
-/// Possible entry types
+/// Possible entry types
enum EntryType {
/// Entry is a directory
Directory,
@@ -75,19 +78,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..378a4d0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,11 +16,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