diff --git a/Cargo.lock b/Cargo.lock index a3a638124..bb1a38dcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -882,6 +882,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" + [[package]] name = "futf" version = "0.1.5" @@ -1553,6 +1559,7 @@ dependencies = [ "comrak", "fake-tty", "fast_qr", + "fs_extra", "futures", "get_if_addrs", "grass", @@ -1564,6 +1571,7 @@ dependencies = [ "maud", "mime", "nanoid", + "once_cell", "percent-encoding", "port_check", "predicates", diff --git a/Cargo.toml b/Cargo.toml index 23771bfda..9ed89a852 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ clap_complete = "4" clap_mangen = "0.2" comrak = { version = "0.15", default-features = false } fast_qr = { version = "0.6", features = ["svg"] } +fs_extra = "1.2.0" futures = "0.3" get_if_addrs = "0.5" hex = "0.4" @@ -43,6 +44,7 @@ log = "0.4" maud = "0.24" mime = "0.3" nanoid = "0.4" +once_cell = "1.16.0" percent-encoding = "2" port_check = "0.1" regex = "1" diff --git a/src/listing.rs b/src/listing.rs index bca531a81..ad4194dd2 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -1,11 +1,16 @@ #![allow(clippy::format_push_string)] +use std::collections::HashMap; +use std::fmt::Display; use std::io; use std::path::{Component, Path, PathBuf}; +use std::sync::{Arc, Mutex}; use std::time::SystemTime; use actix_web::{dev::ServiceResponse, web::Query, HttpMessage, HttpRequest, HttpResponse}; use bytesize::ByteSize; use comrak::{markdown_to_html, ComrakOptions}; +use log::warn; +use once_cell::sync::OnceCell; use percent_encoding::{percent_decode_str, utf8_percent_encode}; use regex::Regex; use serde::Deserialize; @@ -18,6 +23,8 @@ use crate::renderer; use self::percent_encode_sets::PATH_SEGMENT; +static FILE_SIZE_CACHE: OnceCell>>> = OnceCell::new(); + /// "percent-encode sets" as defined by WHATWG specs: /// https://url.spec.whatwg.org/#percent-encoded-bytes mod percent_encode_sets { @@ -78,6 +85,24 @@ pub enum EntryType { File, } +#[derive(Clone, Copy, PartialEq, Eq)] +/// Possible entry size types +pub enum EntrySize { + /// EntryCount is number of entries in a directory + EntryCount(usize), + /// Bytes is number of bytes in a file + Bytes(bytesize::ByteSize), +} + +impl Display for EntrySize { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + EntrySize::EntryCount(count) => write!(f, "{}", count), + EntrySize::Bytes(bytes) => write!(f, "{}", bytes), + } + } +} + /// Entry pub struct Entry { /// Name of the entry @@ -89,8 +114,8 @@ pub struct Entry { /// URL of the entry pub link: String, - /// Size in byte of the entry. Only available for EntryType::File - pub size: Option, + /// Size of the entry + pub size: EntrySize, /// Last modification date pub last_modification_date: Option, @@ -104,7 +129,7 @@ impl Entry { name: String, entry_type: EntryType, link: String, - size: Option, + size: EntrySize, last_modification_date: Option, symlink_info: Option, ) -> Self { @@ -256,12 +281,36 @@ pub fn directory_listing( Err(_) => None, }; + let cache_map = FILE_SIZE_CACHE + .get_or_init(|| { + println!("init cache_map"); + Arc::default() + }) + .clone(); + let size = { + match fs_extra::dir::get_size(&entry.path()) { + Err(_) => EntrySize::EntryCount({ + std::fs::read_dir(entry.path()) + .into_iter() + .take(500_000) + .count() + }), + Ok(result) => { + if let Ok(mut lock) = cache_map.lock() { + lock.insert(entry.path().to_path_buf(), result); + } else { + warn!("Failed to write to cache"); + }; + EntrySize::Bytes(ByteSize::b(result)) + } + } + }; if metadata.is_dir() { entries.push(Entry::new( file_name, EntryType::Directory, file_url, - None, + size, last_modification_date, symlink_dest, )); @@ -270,7 +319,7 @@ pub fn directory_listing( file_name.clone(), EntryType::File, file_url, - Some(ByteSize::b(metadata.len())), + EntrySize::Bytes(ByteSize::b(metadata.len())), last_modification_date, symlink_dest, )); @@ -302,9 +351,19 @@ pub fn directory_listing( 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))) + match (e1.size, e2.size) { + (EntrySize::EntryCount(ref e1_count), EntrySize::EntryCount(ref e2_count)) => { + e2_count.cmp(e1_count) + } + (EntrySize::Bytes(ref e1_bytes), EntrySize::Bytes(ref e2_bytes)) => { + e2_bytes.cmp(e1_bytes) + } + (EntrySize::EntryCount(_), EntrySize::Bytes(_)) => std::cmp::Ordering::Greater, + (EntrySize::Bytes(_), EntrySize::EntryCount(_)) => std::cmp::Ordering::Less, + } + // 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 diff --git a/src/renderer.rs b/src/renderer.rs index 5ba69a222..72b8187ca 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -515,19 +515,23 @@ fn entry_row( } @if !raw { - @if let Some(size) = entry.size { - span.mobile-info.size { - (maud::display(size)) - } + span.mobile-info.size { + (maud::display(&entry.size)) } + // @if let size = entry.size { + // span.mobile-info.size { + // (maud::display(size)) + // } + // } } } } } td.size-cell { - @if let Some(size) = entry.size { - (maud::display(size)) - } + (maud::display(&entry.size)) + // @if let size = entry.size { + // (maud::display(size)) + // } } td.date-cell { @if let Some(modification_date) = convert_to_local(entry.last_modification_date) {