diff options
author | boasting-squirrel <boasting.squirrel@gmail.com> | 2019-02-25 19:22:58 +0000 |
---|---|---|
committer | boasting-squirrel <boasting.squirrel@gmail.com> | 2019-02-25 19:22:58 +0000 |
commit | 6e95f942f1ae6c9c120c95eeb8b1aae1379bcd54 (patch) | |
tree | f7f742418ebc0b64a821a5204abe5889343ab751 | |
parent | Use rust nightly (diff) | |
download | miniserve-6e95f942f1ae6c9c120c95eeb8b1aae1379bcd54.tar.gz miniserve-6e95f942f1ae6c9c120c95eeb8b1aae1379bcd54.zip |
Removed sorting from CLI + added sorting from HTML
-rw-r--r-- | src/args.rs | 18 | ||||
-rw-r--r-- | src/listing.rs | 151 | ||||
-rw-r--r-- | src/main.rs | 17 | ||||
-rw-r--r-- | src/renderer.rs | 34 |
4 files changed, 128 insertions, 92 deletions
diff --git a/src/args.rs b/src/args.rs index 637c224..4f0dbf7 100644 --- a/src/args.rs +++ b/src/args.rs @@ -3,7 +3,6 @@ use std::path::PathBuf; use structopt::StructOpt; use crate::auth; -use crate::listing; /// Possible characters for random routes const ROUTE_ALPHABET: [char; 16] = [ @@ -45,21 +44,6 @@ struct CLIArgs { #[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, @@ -116,7 +100,5 @@ pub fn parse_args() -> crate::MiniserveConfig { path_explicitly_chosen, no_symlinks: args.no_symlinks, random_route, - 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 9af2992..cec0f5e 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -1,34 +1,70 @@ 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::io; use std::path::Path; use std::time::SystemTime; use crate::renderer; -arg_enum! { - #[derive(Clone, Copy, Debug)] - /// Available sorting methods - /// - /// Natural: natural sorting method - /// 1 -> 2 -> 3 -> 11 - /// - /// Alpha: pure alphabetical sorting method - /// 1 -> 11 -> 2 -> 3 - /// - /// DirsFirst: directories are listed first, alphabetical sorting is also applied - /// 1/ -> 2/ -> 3/ -> 11 -> 12 - /// - /// Date: sort by last modification date (most recent first) - pub enum SortingMethods { - Natural, - Alpha, - DirsFirst, - Date +/// Available sorting methods +#[derive(Debug)] +pub enum SortingMethod { + /// Sort by name + Name, + + /// Sort by size + Size, + + /// Sort by last modification date (natural sort: follows alphanumerical order) + Date, +} + +impl SortingMethod { + fn from_str(src: &str) -> Self { + match src { + "name" => SortingMethod::Name, + "size" => SortingMethod::Size, + "date" => SortingMethod::Date, + _ => SortingMethod::Name, + } + } + + pub fn to_string(&self) -> String { + match &self { + SortingMethod::Name => "name", + SortingMethod::Size => "size", + SortingMethod::Date => "date", + } + .to_string() + } +} + +/// Available sorting orders +#[derive(Debug)] +pub enum SortingOrder { + /// Ascending order + Ascending, + + /// Descending order + Descending, +} + +impl SortingOrder { + fn from_str(src: &str) -> Self { + match src { + "desc" => SortingOrder::Descending, + _ => SortingOrder::Ascending, + } + } + + pub fn to_string(&self) -> String { + match &self { + SortingOrder::Ascending => "asc", + SortingOrder::Descending => "desc", + } + .to_string() } } @@ -42,16 +78,6 @@ pub enum EntryType { File, } -impl PartialOrd for EntryType { - fn partial_cmp(&self, other: &EntryType) -> Option<Ordering> { - match (self, other) { - (EntryType::Directory, EntryType::File) => Some(Ordering::Less), - (EntryType::File, EntryType::Directory) => Some(Ordering::Greater), - _ => Some(Ordering::Equal), - } - } -} - /// Entry pub struct Entry { /// Name of the entry @@ -104,8 +130,6 @@ pub fn directory_listing<S>( req: &HttpRequest<S>, skip_symlinks: bool, random_route: Option<String>, - sort_method: SortingMethods, - reverse_sort: bool, ) -> Result<HttpResponse, io::Error> { let title = format!("Index of {}", req.path()); let base = Path::new(req.path()); @@ -113,6 +137,10 @@ pub fn directory_listing<S>( let is_root = base.parent().is_none() || req.path() == random_route; let page_parent = base.parent().map(|p| p.display().to_string()); + let query = req.query(); + let sort_method = query.get("sort").map(|e| SortingMethod::from_str(e)); + let sort_order = query.get("order").map(|e| SortingOrder::from_str(e)); + let mut entries: Vec<Entry> = Vec::new(); for entry in dir.path.read_dir()? { @@ -161,31 +189,44 @@ pub fn directory_listing<S>( } } - 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()); + if let Some(sorting_method) = &sort_method { + match sorting_method { + SortingMethod::Name => entries + .sort_by(|e1, e2| alphanumeric_sort::compare_str(e1.name.clone(), e2.name.clone())), + SortingMethod::Size => entries.sort_by(|e1, e2| { + // If we can't get the size of the entry (directory for instance) + // let's consider it's 0b + e2.size + .unwrap_or_else(|| ByteSize::b(0)) + .cmp(&e1.size.unwrap_or_else(|| ByteSize::b(0))) + }), + SortingMethod::Date => entries.sort_by(|e1, e2| { + // If, for some reason, we can't get the last modification date of an entry + // let's consider it was modified on UNIX_EPOCH (01/01/19270 00:00:00) + e2.last_modification_date + .unwrap_or(SystemTime::UNIX_EPOCH) + .cmp(&e1.last_modification_date.unwrap_or(SystemTime::UNIX_EPOCH)) + }), + }; + } + + if let Some(sorting_order) = &sort_order { + if let SortingOrder::Descending = sorting_order { + entries.reverse() } - SortingMethods::Date => entries.sort_by(|e1, e2| { - // If, for some reason, we can't get the last modification date of an entry - // let's consider it was modified on UNIX_EPOCH (01/01/19270 00:00:00) - e2.last_modification_date - .unwrap_or(SystemTime::UNIX_EPOCH) - .cmp(&e1.last_modification_date.unwrap_or(SystemTime::UNIX_EPOCH)) - }), - }; - - if reverse_sort { - entries.reverse(); } Ok(HttpResponse::Ok() .content_type("text/html; charset=utf-8") - .body(renderer::page(&title, entries, is_root, page_parent).into_string())) + .body( + renderer::page( + &title, + entries, + is_root, + page_parent, + sort_method, + sort_order, + ) + .into_string(), + )) } diff --git a/src/main.rs b/src/main.rs index e9b16df..37392e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,12 +40,6 @@ pub struct MiniserveConfig { /// Enable random route generation pub random_route: Option<String>, - - /// Sort files/directories - pub sort_method: listing::SortingMethods, - - /// Enable inverse sorting - pub reverse_sort: bool, } fn main() { @@ -181,8 +175,6 @@ fn configure_app(app: App<MiniserveConfig>) -> App<MiniserveConfig> { 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; - let reverse_sort = app.state().reverse_sort; if path.is_file() { None } else { @@ -191,14 +183,7 @@ fn configure_app(app: App<MiniserveConfig>) -> App<MiniserveConfig> { .expect("Couldn't create path") .show_files_listing() .files_listing_renderer(move |dir, req| { - listing::directory_listing( - dir, - req, - no_symlinks, - random_route.clone(), - sort_method, - reverse_sort, - ) + listing::directory_listing(dir, req, no_symlinks, random_route.clone()) }), ) } diff --git a/src/renderer.rs b/src/renderer.rs index 82937ab..2f25cad 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -11,6 +11,8 @@ pub fn page( entries: Vec<listing::Entry>, is_root: bool, page_parent: Option<String>, + sort_method: Option<listing::SortingMethod>, + sort_order: Option<listing::SortingOrder>, ) -> Markup { html! { (page_header(page_title)) @@ -18,9 +20,9 @@ pub fn page( h1 { (page_title) } table { thead { - th { "Name" } - th { "Size" } - th { "Last modification" } + th { (build_link("name", "Name", &sort_method, &sort_order)) } + th { (build_link("size", "Size", &sort_method, &sort_order)) } + th { (build_link("date", "Last modification", &sort_method, &sort_order)) } } tbody { @if !is_root { @@ -43,6 +45,32 @@ pub fn page( } } +/// Partial: table header link +fn build_link( + name: &str, + title: &str, + sort_method: &Option<listing::SortingMethod>, + sort_order: &Option<listing::SortingOrder>, +) -> Markup { + let mut link = format!("?sort={}&order=asc", name); + let mut help = format!("Sort by {} in ascending order", name); + + if let Some(method) = sort_method { + if method.to_string() == name { + if let Some(order) = sort_order { + if order.to_string() == "asc" { + link = format!("?sort={}&order=desc", name); + help = format!("Sort by {} in descending order", name); + } + } + } + }; + + html! { + a href=(link) title=(help) { (title) } + } +} + /// Partial: page header fn page_header(page_title: &str) -> Markup { html! { |