mirror of
https://github.com/svenstaro/miniserve
synced 2024-07-08 20:05:56 +00:00
Switched to structopt
This commit is contained in:
parent
e543c5a7c3
commit
fb63c8de43
38
Cargo.lock
generated
38
Cargo.lock
generated
|
@ -535,6 +535,14 @@ dependencies = [
|
|||
"tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hostname"
|
||||
version = "0.1.5"
|
||||
|
@ -716,6 +724,7 @@ dependencies = [
|
|||
"nanoid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1245,6 +1254,26 @@ name = "strsim"
|
|||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "structopt"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "structopt-derive"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.26"
|
||||
|
@ -1604,6 +1633,11 @@ dependencies = [
|
|||
"smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.5"
|
||||
|
@ -1816,6 +1850,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b"
|
||||
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
|
||||
"checksum h2 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ddb2b25a33e231484694267af28fec74ac63b5ccf51ee2065a5e313b834d836e"
|
||||
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||
"checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e"
|
||||
"checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163"
|
||||
"checksum http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1a10e5b573b9a0146545010f50772b9e8b1dd0a256564cc4307694c68832a2f5"
|
||||
|
@ -1902,6 +1937,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
|
||||
"checksum string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b639411d0b9c738748b5397d5ceba08e648f4f1992231aa859af1a017f31f60b"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad348dc73012fcf78c71f06f9d942232cdd4c859d4b6975e27836c3efc0c3"
|
||||
"checksum structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ef98172b1a00b0bec738508d3726540edcbd186d50dfd326f2b1febbb3559f04"
|
||||
"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9"
|
||||
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
|
||||
"checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561"
|
||||
|
@ -1932,6 +1969,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90"
|
||||
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426"
|
||||
"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
|
||||
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
||||
|
|
|
@ -28,4 +28,5 @@ percent-encoding = "1.0.1"
|
|||
htmlescape = "0.3.1"
|
||||
bytesize = "1.0.0"
|
||||
nanoid = "0.2.0"
|
||||
alphanumeric-sort = "1.0.6"
|
||||
alphanumeric-sort = "1.0.6"
|
||||
structopt = "0.2.14"
|
201
src/args.rs
201
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",
|
||||
))
|
||||
#[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 a port is valid
|
||||
fn is_valid_port(port: String) -> Result<(), String> {
|
||||
port.parse::<u16>()
|
||||
.and(Ok(()))
|
||||
.or_else(|e| Err(e.to_string()))
|
||||
}
|
||||
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)?)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user