diff options
author | Sven-Hendrik Haase <svenstaro@gmail.com> | 2019-02-20 01:25:48 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-02-20 01:25:48 +0000 |
commit | 6de9e6234d1eeeba3038c7e809e94cb03b819d52 (patch) | |
tree | 6051a0649b117cb6a4f6ee08f3cb133f9316d5cc | |
parent | Update README.md (diff) | |
parent | Renamed humanize_duration to humanize_systemtime (diff) | |
download | miniserve-6de9e6234d1eeeba3038c7e809e94cb03b819d52.tar.gz miniserve-6de9e6234d1eeeba3038c7e809e94cb03b819d52.zip |
Merge pull request #38 from boastful-squirrel/date
Display last modification date in listing + provides a way to sort by modification date
-rw-r--r-- | Cargo.lock | 11 | ||||
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | src/listing.rs | 90 |
4 files changed, 100 insertions, 8 deletions
@@ -276,6 +276,14 @@ dependencies = [ ] [[package]] +name = "chrono-humanize" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "clap" version = "2.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -719,6 +727,8 @@ dependencies = [ "alphanumeric-sort 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytesize 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "nanoid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1821,6 +1831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "4390a3b5f4f6bce9c1d0c00128379df433e53777fdd30e92f16a529332baec4e" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" +"checksum chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2ff48a655fe8d2dae9a39e66af7fd8ff32a879e8c4e27422c25596a8b5e90d" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1465f8134efa296b4c19db34d909637cb2bf0f7aaf21299e23e18fa29ac557cf" @@ -29,4 +29,6 @@ htmlescape = "0.3.1" bytesize = "1.0.0" nanoid = "0.2.0" alphanumeric-sort = "1.0.6" -structopt = "0.2.14"
\ No newline at end of file +structopt = "0.2.14" +chrono = "0.4.6" +chrono-humanize = "0.0.11"
\ No newline at end of file @@ -56,6 +56,9 @@ Sometimes this is just a more practical and quick way than doing things properly # 11 # 3 + miniserve --sort=date /tmp/myshare + # list files in chronological order (most recent files on top of the list) + miniserve --reverse /tmp/myshare # 11 # 3 diff --git a/src/listing.rs b/src/listing.rs index 056c847..21aebec 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -1,5 +1,7 @@ use actix_web::{fs, HttpRequest, HttpResponse, Result}; use bytesize::ByteSize; +use chrono::{DateTime, Duration, Utc}; +use chrono_humanize::{Accuracy, HumanTime, Tense}; use clap::{_clap_count_exprs, arg_enum}; use htmlescape::encode_minimal as escape_html_entity; use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET}; @@ -7,6 +9,7 @@ use std::cmp::Ordering; use std::fmt::Write as FmtWrite; use std::io; use std::path::Path; +use std::time::SystemTime; arg_enum! { #[derive(Clone, Copy, Debug)] @@ -20,10 +23,13 @@ arg_enum! { /// /// 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 } } @@ -60,6 +66,9 @@ struct Entry { /// Size in byte of the entry. Only available for EntryType::File size: Option<bytesize::ByteSize>, + + /// Last modification date + last_modification_date: Option<SystemTime>, } impl Entry { @@ -68,12 +77,14 @@ impl Entry { entry_type: EntryType, link: String, size: Option<bytesize::ByteSize>, + last_modification_date: Option<SystemTime>, ) -> Self { Entry { name, entry_type, link, size, + last_modification_date, } } } @@ -128,14 +139,26 @@ pub fn directory_listing<S>( if skip_symlinks && metadata.file_type().is_symlink() { continue; } + let last_modification_date = match metadata.modified() { + Ok(date) => Some(date), + Err(_) => None, + }; + if metadata.is_dir() { - entries.push(Entry::new(file_name, EntryType::Directory, file_url, None)); + entries.push(Entry::new( + file_name, + EntryType::Directory, + file_url, + None, + last_modification_date, + )); } else { entries.push(Entry::new( file_name, EntryType::File, file_url, Some(ByteSize::b(metadata.len())), + last_modification_date, )); } } else { @@ -155,28 +178,39 @@ pub fn directory_listing<S>( entries.sort_by_key(|e| e.name.clone()); entries.sort_by(|e1, e2| e1.entry_type.partial_cmp(&e2.entry_type).unwrap()); } + 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(); } - for entry in entries { + let (modification_date, modification_time) = convert_to_utc(entry.last_modification_date); + match entry.entry_type { EntryType::Directory => { let _ = write!( body, - "<tr><td><a class=\"directory\" href=\"{}\">{}/</a></td><td></td></tr>", - entry.link, entry.name + "<tr><td><a class=\"directory\" href=\"{}\">{}/</a></td><td></td><td class=\"date-cell\"><span>{}</span><span>{}</span><span>{}</span></td></tr>", + entry.link, entry.name, modification_date, modification_time, humanize_systemtime(entry.last_modification_date) ); } EntryType::File => { let _ = write!( body, - "<tr><td><a class=\"file\" href=\"{}\">{}</a></td><td>{}</td></tr>", + "<tr><td><a class=\"file\" href=\"{}\">{}</a></td><td>{}</td><td class=\"date-cell\"><span>{}</span><span>{}</span><span>{}</span></td></tr>", entry.link, entry.name, - entry.size.unwrap() + entry.size.unwrap(), + modification_date, + modification_time, + humanize_systemtime(entry.last_modification_date) ); } } @@ -210,6 +244,7 @@ pub fn directory_listing<S>( color: #777c82;\ text-align: left;\ line-height: 1.125rem;\ + width: 33.333%;\ }}\ table thead tr th {{\ padding: 0.5rem 0.625rem 0.625rem;\ @@ -236,6 +271,17 @@ pub fn directory_listing<S>( a:visited {{\ color: #8e44ad;\ }}\ + td.date-cell {{\ + display: flex;\ + width: calc(100% - 1.25rem);\ + }}\ + td.date-cell span:first-of-type,\ + td.date-cell span:nth-of-type(2) {{\ + flex-basis:4.5rem;\ + }}\ + td.date-cell span:nth-of-type(3) {{\ + color: #c5c5c5;\ + }}\ @media (max-width: 600px) {{\ h1 {{\ font-size: 1.375em;\ @@ -250,7 +296,7 @@ pub fn directory_listing<S>( </head>\ <body><h1>{}</h1>\ <table>\ - <thead><th>Name</th><th>Size</th></thead>\ + <thead><th>Name</th><th>Size</th><th>Last modification</th></thead>\ <tbody>\ {}\ </tbody></table></body>\n</html>", @@ -260,3 +306,33 @@ pub fn directory_listing<S>( .content_type("text/html; charset=utf-8") .body(html)) } + +/// Converts a SystemTime object to a strings tuple (date, time) +/// Date is formatted as %e %b, e.g. Jul 12 +/// Time is formatted as %R, e.g. 22:34 +/// +/// If no SystemTime was given, returns a tuple containing empty strings +fn convert_to_utc(src_time: Option<SystemTime>) -> (String, String) { + src_time + .map(|time| DateTime::<Utc>::from(time)) + .map(|date_time| { + ( + date_time.format("%e %b").to_string(), + date_time.format("%R").to_string(), + ) + }) + .unwrap_or_default() +} + +/// Converts a SystemTime to a string readable by a human, +/// i.e. calculates the duration between now() and the given SystemTime, +/// and gives a rough approximation of the elapsed time since +/// +/// If no SystemTime was given, returns an empty string +fn humanize_systemtime(src_time: Option<SystemTime>) -> String { + src_time + .and_then(|std_time| SystemTime::now().duration_since(std_time).ok()) + .and_then(|from_now| Duration::from_std(from_now).ok()) + .map(|duration| HumanTime::from(duration).to_text_en(Accuracy::Rough, Tense::Past)) + .unwrap_or_default() +} |