From 4585d5456c8ad088b96ca51039b4e8274f7ccf38 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Mon, 4 Feb 2019 20:28:06 +0100 Subject: Implemented sorting --- src/main.rs | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 128 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index b7f9be9..e7392bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,10 +6,12 @@ use clap::{crate_authors, crate_description, crate_name, crate_version}; use htmlescape::encode_minimal as escape_html_entity; use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET}; use simplelog::{Config, LevelFilter, TermLogger}; +use std::cmp::Ordering; use std::fmt::Write as FmtWrite; use std::io::{self, Write}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; use std::path::{Path, PathBuf}; +use std::str::FromStr; use std::thread; use std::time::Duration; use yansi::{Color, Paint}; @@ -29,6 +31,13 @@ struct BasicAuthParams { password: String, } +#[derive(Clone, Debug)] +enum SortingMethods { + Natural, + Alpha, + DirsFirst, +} + #[derive(Clone, Debug)] pub struct MiniserveConfig { verbose: bool, @@ -39,6 +48,59 @@ pub struct MiniserveConfig { path_explicitly_chosen: bool, no_symlinks: bool, random_route: Option, + sort_method: SortingMethods, +} + +#[derive(PartialEq)] +enum EntryType { + Directory, + File, +} + +impl PartialOrd for EntryType { + fn partial_cmp(&self, other: &EntryType) -> Option { + match (self, other) { + (EntryType::Directory, EntryType::File) => Some(Ordering::Less), + (EntryType::File, EntryType::Directory) => Some(Ordering::Greater), + _ => Some(Ordering::Equal), + } + } +} + +struct Entry { + name: String, + entry_type: EntryType, + link: String, + size: Option, +} + +impl Entry { + fn new( + name: String, + entry_type: EntryType, + link: String, + size: Option, + ) -> Self { + Entry { + name, + entry_type, + link, + size, + } + } +} + +impl FromStr for SortingMethods { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "natural" => Ok(SortingMethods::Natural), + "alpha" => Ok(SortingMethods::Alpha), + "dirsfirst" => Ok(SortingMethods::DirsFirst), + _ => Err(()), + } + } } /// Decode a HTTP basic auth string into a tuple of username and password. @@ -140,6 +202,14 @@ pub fn parse_args() -> MiniserveConfig { .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 results"), + ) .arg( Arg::with_name("no-symlinks") .short("P") @@ -180,6 +250,12 @@ pub fn parse_args() -> MiniserveConfig { None }; + let sort_method = matches + .value_of("sort") + .unwrap() + .parse::() + .unwrap(); + MiniserveConfig { verbose, path: PathBuf::from(path.unwrap_or(".")), @@ -189,6 +265,7 @@ pub fn parse_args() -> MiniserveConfig { path_explicitly_chosen: path.is_some(), no_symlinks, random_route, + sort_method, } } @@ -202,6 +279,7 @@ fn configure_app(app: App) -> App { let path = &app.state().path; let no_symlinks = app.state().no_symlinks; let random_route = app.state().random_route.clone(); + let sort_method = app.state().sort_method.clone(); if path.is_file() { None } else { @@ -210,7 +288,13 @@ fn configure_app(app: App) -> App { .expect("Couldn't create path") .show_files_listing() .files_listing_renderer(move |dir, req| { - directory_listing(dir, req, no_symlinks, random_route.clone()) + directory_listing( + dir, + req, + no_symlinks, + random_route.clone(), + sort_method.clone(), + ) }), ) } @@ -399,6 +483,7 @@ fn directory_listing( req: &HttpRequest, skip_symlinks: bool, random_route: Option, + sort_method: SortingMethods, ) -> Result { let index_of = format!("Index of {}", req.path()); let mut body = String::new(); @@ -415,6 +500,8 @@ fn directory_listing( } } + let mut entries: Vec = Vec::new(); + for entry in dir.path.read_dir()? { if dir.is_visible(&entry) { let entry = entry.unwrap(); @@ -433,21 +520,15 @@ fn directory_listing( if skip_symlinks && metadata.file_type().is_symlink() { continue; } - if metadata.is_dir() { - let _ = write!( - body, - "{}/", - file_url, file_name - ); + entries.push(Entry::new(file_name, EntryType::Directory, file_url, None)); } else { - let _ = write!( - body, - "{}{}", - file_url, + entries.push(Entry::new( file_name, - ByteSize::b(metadata.len()) - ); + EntryType::File, + file_url, + Some(ByteSize::b(metadata.len())), + )); } } else { continue; @@ -455,6 +536,40 @@ fn directory_listing( } } + match sort_method { + SortingMethods::Natural => entries + .sort_by(|e1, e2| alphanumeric_sort::compare_str(e1.name.clone(), e2.name.clone())), + SortingMethods::Alpha => { + entries.sort_by(|e1, e2| e1.entry_type.partial_cmp(&e2.entry_type).unwrap()); + entries.sort_by_key(|e| e.name.clone()) + } + SortingMethods::DirsFirst => { + entries.sort_by_key(|e| e.name.clone()); + entries.sort_by(|e1, e2| e1.entry_type.partial_cmp(&e2.entry_type).unwrap()); + } + }; + + for entry in entries { + match entry.entry_type { + EntryType::Directory => { + let _ = write!( + body, + "{}/", + entry.link, entry.name + ); + } + EntryType::File => { + let _ = write!( + body, + "{}{}", + entry.link, + entry.name, + entry.size.unwrap() + ); + } + } + } + let html = format!( "\ \ -- cgit v1.2.3 From 47d67bb037383ffb6c979136a3d486d33f74a11b Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Mon, 4 Feb 2019 22:42:30 +0100 Subject: Added --reverse option to reverse sort order --- src/main.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index e7392bd..24f7d82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,6 +49,7 @@ pub struct MiniserveConfig { no_symlinks: bool, random_route: Option, sort_method: SortingMethods, + reverse_sort: bool, } #[derive(PartialEq)] @@ -208,7 +209,12 @@ pub fn parse_args() -> MiniserveConfig { .long("sort") .possible_values(&["natural", "alpha", "dirsfirst"]) .default_value("natural") - .help("Sort results"), + .help("Sort files"), + ) + .arg( + Arg::with_name("reverse") + .long("reverse") + .help("Reverse sorting order"), ) .arg( Arg::with_name("no-symlinks") @@ -256,6 +262,8 @@ pub fn parse_args() -> MiniserveConfig { .parse::() .unwrap(); + let reverse_sort = matches.is_present("reverse"); + MiniserveConfig { verbose, path: PathBuf::from(path.unwrap_or(".")), @@ -266,6 +274,7 @@ pub fn parse_args() -> MiniserveConfig { no_symlinks, random_route, sort_method, + reverse_sort, } } @@ -280,6 +289,7 @@ fn configure_app(app: App) -> App { let no_symlinks = app.state().no_symlinks; let random_route = app.state().random_route.clone(); let sort_method = app.state().sort_method.clone(); + let reverse_sort = app.state().reverse_sort; if path.is_file() { None } else { @@ -294,6 +304,7 @@ fn configure_app(app: App) -> App { no_symlinks, random_route.clone(), sort_method.clone(), + reverse_sort, ) }), ) @@ -484,6 +495,7 @@ fn directory_listing( skip_symlinks: bool, random_route: Option, sort_method: SortingMethods, + reverse_sort: bool, ) -> Result { let index_of = format!("Index of {}", req.path()); let mut body = String::new(); @@ -549,6 +561,10 @@ fn directory_listing( } }; + if reverse_sort { + entries.reverse(); + } + for entry in entries { match entry.entry_type { EntryType::Directory => { -- cgit v1.2.3 From ecbfaaa41d761a6828a1f2215d057157666ef067 Mon Sep 17 00:00:00 2001 From: boasting-squirrel Date: Tue, 5 Feb 2019 07:49:16 +0100 Subject: Fixed missing slash in CSS causing new line --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index 24f7d82..d3fb3b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -630,7 +630,7 @@ fn directory_listing( a.root, a.root:visited {{\ font-weight: bold;\ color: #777c82;\ - }} + }}\ a.directory {{\ font-weight: bold;\ }}\ -- cgit v1.2.3