aboutsummaryrefslogtreecommitdiffstats
path: root/src/listing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/listing.rs')
-rw-r--r--src/listing.rs238
1 files changed, 17 insertions, 221 deletions
diff --git a/src/listing.rs b/src/listing.rs
index 629a117..9af2992 100644
--- a/src/listing.rs
+++ b/src/listing.rs
@@ -1,16 +1,15 @@
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};
use std::cmp::Ordering;
-use std::fmt::Write as FmtWrite;
use std::io;
use std::path::Path;
use std::time::SystemTime;
+use crate::renderer;
+
arg_enum! {
#[derive(Clone, Copy, Debug)]
/// Available sorting methods
@@ -35,7 +34,7 @@ arg_enum! {
#[derive(PartialEq)]
/// Possible entry types
-enum EntryType {
+pub enum EntryType {
/// Entry is a directory
Directory,
@@ -54,21 +53,21 @@ impl PartialOrd for EntryType {
}
/// Entry
-struct Entry {
+pub struct Entry {
/// Name of the entry
- name: String,
+ pub name: String,
/// Type of the entry
- entry_type: EntryType,
+ pub entry_type: EntryType,
/// URL of the entry
- link: String,
+ pub link: String,
/// Size in byte of the entry. Only available for EntryType::File
- size: Option<bytesize::ByteSize>,
+ pub size: Option<bytesize::ByteSize>,
/// Last modification date
- last_modification_date: Option<SystemTime>,
+ pub last_modification_date: Option<SystemTime>,
}
impl Entry {
@@ -87,6 +86,10 @@ impl Entry {
last_modification_date,
}
}
+
+ pub fn is_dir(&self) -> bool {
+ self.entry_type == EntryType::Directory
+ }
}
pub fn file_handler(req: &HttpRequest<crate::MiniserveConfig>) -> Result<fs::NamedFile> {
@@ -104,20 +107,11 @@ pub fn directory_listing<S>(
sort_method: SortingMethods,
reverse_sort: bool,
) -> Result<HttpResponse, io::Error> {
- let index_of = format!("Index of {}", req.path());
- let mut body = String::new();
+ let title = format!("Index of {}", req.path());
let base = Path::new(req.path());
let random_route = format!("/{}", random_route.unwrap_or_default());
-
- if let Some(parent) = base.parent() {
- if req.path() != random_route {
- let _ = write!(
- body,
- "<tr><td><a class=\"root\" href=\"{}\">..</a></td><td></td></tr>",
- parent.display()
- );
- }
- }
+ let is_root = base.parent().is_none() || req.path() == random_route;
+ let page_parent = base.parent().map(|p| p.display().to_string());
let mut entries: Vec<Entry> = Vec::new();
@@ -190,206 +184,8 @@ pub fn directory_listing<S>(
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>\
- <span class=\"mobile-info\">\
- <strong>Last modification:</strong> {} {}\
- </span>\
- </td>\
- <td></td>\
- <td class=\"date-cell\">\
- <span>{}</span>\
- <span>{}</span>\
- <span>{}</span>\
- </td>\
- </tr>",
- entry.link,
- entry.name,
- modification_date,
- modification_time,
- modification_date,
- modification_time,
- humanize_systemtime(entry.last_modification_date)
- );
- }
- EntryType::File => {
- let _ = write!(
- body,
- "<tr>\
- <td>\
- <a class=\"file\" href=\"{}\">{}</a>\
- <span class=\"mobile-info\">\
- <strong>Size:</strong> {}\
- </span>\
- <span class=\"mobile-info\">\
- <strong>Last modification:</strong> {} {} <span class=\"history\">({})</span>\
- </span>\
- </td>\
- <td>\
- {}\
- </td>\
- <td class=\"date-cell\">\
- <span>{}</span>\
- <span>{}</span>\
- <span>{}</span>\
- </td>\
- </tr>",
- entry.link,
- entry.name,
- entry.size.unwrap(),
- modification_date,
- modification_time,
- humanize_systemtime(entry.last_modification_date),
- entry.size.unwrap(),
- modification_date,
- modification_time,
- humanize_systemtime(entry.last_modification_date)
- );
- }
- }
- }
-
- let html = format!(
- "<html>\
- <head>\
- <title>{}</title>\
- <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\
- <style>\
- body {{\
- margin: 0;\
- font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto,\"Helvetica Neue\", Helvetica, Arial, sans-serif;\
- font-weight: 300;\
- color: #444444;\
- padding: 0.125rem;\
- }}\
- table {{\
- width: 100%;\
- background: white;\
- border: 0;\
- table-layout: auto;\
- }}\
- table thead {{\
- background: #efefef;\
- }}\
- table tr th,\
- table tr td {{\
- padding: 0.5625rem 0.625rem;\
- font-size: 0.875rem;\
- color: #777c82;\
- text-align: left;\
- line-height: 1.125rem;\
- width: 33.333%;\
- }}\
- table thead tr th {{\
- padding: 0.5rem 0.625rem 0.625rem;\
- font-weight: bold;\
- color: #444444;\
- }}\
- table tr:nth-child(even) {{\
- background: #f6f6f6;\
- }}\
- a {{\
- text-decoration: none;\
- color: #3498db;\
- }}\
- a.root, a.root:visited {{\
- font-weight: bold;\
- color: #777c82;\
- }}\
- a.directory {{\
- font-weight: bold;\
- }}\
- a:hover {{\
- text-decoration: underline;\
- }}\
- 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), .history {{\
- color: #c5c5c5;\
- }}\
- .file, .directory {{\
- display: block;\
- }}\
- .mobile-info {{\
- display: none;\
- }}\
- @media (max-width: 600px) {{\
- h1 {{\
- font-size: 1.375em;\
- }}\
- td:not(:nth-child(1)), th:not(:nth-child(1)){{\
- display: none;\
- }}\
- .mobile-info {{\
- display: block;\
- }}\
- .file, .directory{{\
- padding-bottom: 0.5rem;\
- }}\
- }}\
- @media (max-width: 400px) {{\
- h1 {{\
- font-size: 1.375em;\
- }}\
- }}\
- </style>\
- </head>\
- <body><h1>{}</h1>\
- <table>\
- <thead><th>Name</th><th>Size</th><th>Last modification</th></thead>\
- <tbody>\
- {}\
- </tbody></table></body>\n</html>",
- index_of, index_of, body
- );
Ok(HttpResponse::Ok()
.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()
+ .body(renderer::page(&title, entries, is_root, page_parent).into_string()))
}