aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/args.rs18
-rw-r--r--src/listing.rs151
-rw-r--r--src/main.rs17
-rw-r--r--src/renderer.rs34
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! {