use crate::{
error::{ParseResultExt, RegexResultExt, ReqwestResultExt, Result},
httpdirectoryentry::{CompareField, HttpDirectoryEntry},
requests::Request,
scrape::scrape_body,
stats::Stats,
};
use regex::Regex;
use reqwest::Url;
use std::fmt;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tracing::{debug, error, trace};
#[derive(Debug, Clone)]
pub struct HttpDirectory {
entries: Vec<HttpDirectoryEntry>,
url: Arc<String>,
request: Arc<Request>,
timings: Arc<Timings>,
}
#[derive(Debug, Default, Clone)]
struct Timings {
http_request: Duration,
get_entries: Duration,
}
impl Timings {
fn new(http_request: Duration, get_entries: Duration) -> Self {
Timings {
http_request,
get_entries,
}
}
}
impl HttpDirectory {
#[cfg_attr(feature = "hotpath", hotpath::measure)]
pub async fn new(url: &str, timeout_s: Option<u64>) -> Result<Self> {
let now = Instant::now();
let client = Request::new(timeout_s)?;
let response = client.get(url).await?;
let http_request = now.elapsed();
trace!("Response to get '{url}': {response:?}");
let now = Instant::now();
let entries = get_entries_from_body(&response.text().await.with_url(url)?);
let get_entries = now.elapsed();
let timings = Timings::new(http_request, get_entries);
Ok(HttpDirectory {
entries,
url: Arc::new(url.to_string()),
request: Arc::new(client),
timings: Arc::new(timings),
})
}
#[cfg_attr(feature = "hotpath", hotpath::measure)]
pub async fn cd(mut self, dir: &str) -> Result<Self> {
let url =
Url::parse(&self.url).with_url(&self.url)?.join(dir).with_url(&format!("{}/{dir}", &self.url))?.to_string();
debug!("cd is going to {url}");
let now = Instant::now();
let response = self.request.get(&url).await?;
let http_request = now.elapsed();
let now = Instant::now();
let entries = get_entries_from_body(&response.text().await.with_url(&self.url)?);
let get_entries = now.elapsed();
let timings = Timings::new(http_request, get_entries);
self.entries = entries;
self.timings = Arc::new(timings);
self.url = Arc::new(url);
Ok(self)
}
#[must_use]
#[allow(clippy::needless_pass_by_value)]
pub fn sort_by_name(mut self, ascending: bool) -> Self {
self.entries.sort_by(|a, b| a.cmp_by_field(b, &CompareField::Name, ascending));
self
}
#[must_use]
#[allow(clippy::needless_pass_by_value)]
pub fn sort_by_date(mut self, ascending: bool) -> Self {
self.entries.sort_by(|a, b| a.cmp_by_field(b, &CompareField::Date, ascending));
self
}
#[must_use]
#[allow(clippy::needless_pass_by_value)]
#[cfg_attr(feature = "hotpath", hotpath::measure)]
pub fn sort_by_size(mut self, ascending: bool) -> Self {
self.entries.sort_by(|a, b| a.cmp_by_field(b, &CompareField::Size, ascending));
self
}
#[cfg_attr(feature = "hotpath", hotpath::measure)]
#[must_use]
pub fn filtering<F>(&self, mut f: F) -> Self
where
F: FnMut(&HttpDirectoryEntry) -> bool,
{
let entries = self.entries.iter().filter(|entry| f(entry)).cloned().collect();
HttpDirectory {
entries,
url: Arc::clone(&self.url),
request: Arc::clone(&self.request),
timings: Arc::clone(&self.timings),
}
}
#[must_use]
pub fn dirs(&self) -> Self {
self.filtering(HttpDirectoryEntry::is_directory)
}
#[must_use]
pub fn files(&self) -> Self {
self.filtering(HttpDirectoryEntry::is_file)
}
#[must_use]
pub fn parent_directory(&self) -> Self {
self.filtering(HttpDirectoryEntry::is_parent_directory)
}
#[must_use]
pub fn last(&self) -> Option<&HttpDirectoryEntry> {
self.entries.last()
}
#[must_use]
pub fn first(&self) -> Option<&HttpDirectoryEntry> {
self.entries.first()
}
#[must_use]
pub fn stats(&self) -> Stats {
let mut stats = Stats::new();
for entry in self.entries() {
match entry {
HttpDirectoryEntry::ParentDirectory(_) => stats.add_parent_directory(),
HttpDirectoryEntry::Directory(dir) => stats.add_directory(dir.date()),
HttpDirectoryEntry::File(file) => stats.add_file(file.date(), file.size()),
};
}
stats
}
pub fn filter_by_name(&self, regex: &str) -> Result<Self> {
let re = Regex::new(regex).with_regex(regex)?;
Ok(self.filtering(|e| e.is_match_by_name(&re)))
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn entries(&self) -> &Vec<HttpDirectoryEntry> {
&self.entries
}
#[must_use]
pub fn get_url(&self) -> Arc<String> {
self.url.clone()
}
#[must_use]
pub fn http_request_time(&self) -> Duration {
self.timings.http_request
}
#[must_use]
pub fn get_entries_time(&self) -> Duration {
self.timings.get_entries
}
#[must_use]
pub fn total_time(&self) -> Duration {
self.timings.get_entries + self.timings.http_request
}
}
impl fmt::Display for HttpDirectory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"Processed '{}' in {:.2?} ({:.2?} + {:.2?})",
&self.url,
self.total_time(),
self.http_request_time(),
self.get_entries_time()
)?;
for entry in &self.entries {
writeln!(f, "{entry}")?;
}
Ok(())
}
}
impl Default for HttpDirectory {
fn default() -> Self {
HttpDirectory {
entries: vec![],
url: Arc::new(String::new()),
request: Arc::new(Request::default()),
timings: Arc::new(Timings::default()),
}
}
}
fn entries_from_body(body: &str) -> Vec<HttpDirectoryEntry> {
match scrape_body(body) {
Ok(entries) => entries,
Err(e) => {
error!("Error getting entries: {e}");
vec![]
}
}
}
#[cfg(any(test, feature = "test-helpers"))]
pub fn get_entries_from_body(body: &str) -> Vec<HttpDirectoryEntry> {
entries_from_body(body)
}
#[cfg(not(any(test, feature = "test-helpers")))]
#[cfg_attr(feature = "hotpath", hotpath::measure)]
pub(crate) fn get_entries_from_body(body: &str) -> Vec<HttpDirectoryEntry> {
entries_from_body(body)
}
#[cfg(test)]
mod tests {
use {
super::{HttpDirectory, HttpDirectoryEntry},
crate::{
httpdirectoryentry::{EntryType, assert_entry},
stats::Stats,
},
unwrap_unreachable::UnwrapUnreachable,
};
#[tokio::test]
async fn test_httpdirectory_no_base_url() {
let httpdir = HttpDirectory::default();
match httpdir.cd("/dir").await {
Ok(_) => panic!("This test should return Err()"),
Err(e) => assert_eq!(e.to_string(), "Error while parsing url '':\n -> relative URL without a base"),
}
}
fn prepare_httpdir() -> HttpDirectory {
let mut httpdir = HttpDirectory::default();
httpdir.entries.push(HttpDirectoryEntry::new("dir1", "2025-01-26 12:54", "-", "dir1/"));
httpdir.entries.push(HttpDirectoryEntry::new("test2", "2025-02-16 13:37", "-", "test2/"));
httpdir.entries.push(HttpDirectoryEntry::new("debian3", "2025-03-01 07:11", "-", "debian3/"));
httpdir.entries.push(HttpDirectoryEntry::new("entry4", "2025-01-02 12:32", "-", "entry4/"));
httpdir.entries.push(HttpDirectoryEntry::new("file1", "1987-10-09 04:37", "123", "file1/"));
httpdir.entries.push(HttpDirectoryEntry::new("files2", "2023-01-01 00:00", "2345", "files2/"));
httpdir.entries.push(HttpDirectoryEntry::new("entry3", "2025-07-17 23:59", "67K", "entry3/"));
httpdir.entries.push(HttpDirectoryEntry::new("debian4", "2024-12-08 08:22", "123M", "debian4/"));
httpdir.entries.push(HttpDirectoryEntry::new("parent directory", "2025-01-26 12:54", "-", "../"));
httpdir
}
#[test]
fn test_httpdirectory_first_and_last() {
let httpdir = prepare_httpdir();
if let Some(entry) = httpdir.first() {
assert_entry(entry, &EntryType::Directory, "dir1", 0, "2025-01-26 12:54");
} else {
panic!("This test should return an entry");
}
if let Some(entry) = httpdir.last() {
assert_entry(entry, &EntryType::ParentDirectory, "../", 0, "2025-01-26 12:54");
} else {
panic!("This test should return an entry");
}
let httpdir = httpdir.files();
if let Some(entry) = httpdir.first() {
assert_entry(entry, &EntryType::File, "file1", 123, "1987-10-09 04:37");
} else {
panic!("This test should return an entry");
}
if let Some(entry) = httpdir.last() {
assert_entry(entry, &EntryType::File, "debian4", 128_974_848, "2024-12-08 08:22");
} else {
panic!("This test should return an entry");
}
}
#[test]
fn test_httpdirectory_dirs() {
let httpdir = prepare_httpdir().dirs();
assert!(!httpdir.is_empty());
assert_eq!(httpdir.len(), 4);
let entries = httpdir.entries();
assert_entry(&entries[0], &EntryType::Directory, "dir1", 0, "2025-01-26 12:54");
assert_entry(&entries[1], &EntryType::Directory, "test2", 0, "2025-02-16 13:37");
assert_entry(&entries[2], &EntryType::Directory, "debian3", 0, "2025-03-01 07:11");
assert_entry(&entries[3], &EntryType::Directory, "entry4", 0, "2025-01-02 12:32");
let output = format!("{httpdir}");
assert_eq!(
output,
r##"Processed '' in 0.00ns (0.00ns + 0.00ns)
DIR - 2025-01-26 12:54 dir1
DIR - 2025-02-16 13:37 test2
DIR - 2025-03-01 07:11 debian3
DIR - 2025-01-02 12:32 entry4
"##
);
}
#[test]
fn test_httpdirectory_files() {
let httpdir = prepare_httpdir().files();
assert_eq!(httpdir.len(), 4);
let entries = httpdir.entries();
assert_entry(&entries[0], &EntryType::File, "file1", 123, "1987-10-09 04:37");
assert_entry(&entries[1], &EntryType::File, "files2", 2_345, "2023-01-01 00:00");
assert_entry(&entries[2], &EntryType::File, "entry3", 68_608, "2025-07-17 23:59");
assert_entry(&entries[3], &EntryType::File, "debian4", 128_974_848, "2024-12-08 08:22");
}
#[test]
fn test_httpdirectory_filter_by_name_simple_regex() {
let httpdir = prepare_httpdir().filter_by_name("debian").unreachable();
assert_eq!(httpdir.len(), 2);
let entries = httpdir.entries();
assert_entry(&entries[0], &EntryType::Directory, "debian3", 0, "2025-03-01 07:11");
assert_entry(&entries[1], &EntryType::File, "debian4", 128_974_848, "2024-12-08 08:22");
}
#[test]
fn test_httpdirectory_filter_by_name_bad_regex() {
match prepare_httpdir().filter_by_name("deb-[n+-*") {
Ok(_) => panic!("This call must return an Err(), not Ok()"),
Err(e) => assert_eq!(
e.to_string(),
"Regular expression failed to compile 'deb-[n+-*':\n -> regex parse error:\n deb-[n+-*\n ^^^\nerror: invalid character class range, the start must be <= the end"
),
}
}
#[test]
fn test_httpdirectory_filter_by_name_less_simple_regex() {
let httpdir = prepare_httpdir().filter_by_name(r#"debian\d|entry|file\d"#).unreachable();
assert_eq!(httpdir.len(), 5);
let entries = httpdir.entries();
assert_entry(&entries[0], &EntryType::Directory, "debian3", 0, "2025-03-01 07:11");
assert_entry(&entries[1], &EntryType::Directory, "entry4", 0, "2025-01-02 12:32");
assert_entry(&entries[2], &EntryType::File, "file1", 123, "1987-10-09 04:37");
assert_entry(&entries[3], &EntryType::File, "entry3", 68_608, "2025-07-17 23:59");
assert_entry(&entries[4], &EntryType::File, "debian4", 128_974_848, "2024-12-08 08:22");
}
#[test]
fn test_httpdirectory_parent_directory() {
let httpdir = prepare_httpdir().parent_directory();
let entries = httpdir.entries();
assert_eq!(httpdir.len(), 1);
assert_entry(&entries[0], &EntryType::ParentDirectory, "../", 0, "0000-00-00 00:00");
}
#[test]
fn test_httpdirectory_nothing() {
let httpdir = prepare_httpdir().dirs().files();
assert_eq!(httpdir.len(), 0);
}
#[test]
fn test_httpdirectory_sort_by_name() {
let httpdir = prepare_httpdir().sort_by_name(true);
let entries = httpdir.entries();
assert_entry(&entries[0], &EntryType::ParentDirectory, "../", 0, "0000-00-00 00:00");
assert_entry(&entries[1], &EntryType::Directory, "debian3", 0, "2025-03-01 07:11");
assert_entry(&entries[2], &EntryType::File, "debian4", 128_974_848, "2024-12-08 08:22");
assert_entry(&entries[3], &EntryType::Directory, "dir1", 0, "2025-01-26 12:54");
assert_entry(&entries[4], &EntryType::File, "entry3", 6_8608, "2025-07-17 23:59");
assert_entry(&entries[5], &EntryType::Directory, "entry4", 0, "2025-01-02 12:32");
assert_entry(&entries[6], &EntryType::File, "file1", 123, "1987-10-09 04:37");
assert_entry(&entries[7], &EntryType::File, "files2", 2_345, "2023-01-01 00:00");
assert_entry(&entries[8], &EntryType::Directory, "test2", 0, "2025-02-16 13:37");
let output = format!("{httpdir}");
assert_eq!(
output,
r##"Processed '' in 0.00ns (0.00ns + 0.00ns)
DIR - ..
DIR - 2025-03-01 07:11 debian3
FILE 123M 2024-12-08 08:22 debian4
DIR - 2025-01-26 12:54 dir1
FILE 67K 2025-07-17 23:59 entry3
DIR - 2025-01-02 12:32 entry4
FILE 123 1987-10-09 04:37 file1
FILE 2345 2023-01-01 00:00 files2
DIR - 2025-02-16 13:37 test2
"##
);
let httpdir = httpdir.sort_by_name(false);
let entries = httpdir.entries();
assert_entry(&entries[0], &EntryType::ParentDirectory, "../", 0, "0000-00-00 00:00");
assert_entry(&entries[1], &EntryType::Directory, "test2", 0, "2025-02-16 13:37");
assert_entry(&entries[2], &EntryType::File, "files2", 2_345, "2023-01-01 00:00");
assert_entry(&entries[3], &EntryType::File, "file1", 123, "1987-10-09 04:37");
assert_entry(&entries[4], &EntryType::Directory, "entry4", 0, "2025-01-02 12:32");
assert_entry(&entries[5], &EntryType::File, "entry3", 68_608, "2025-07-17 23:59");
assert_entry(&entries[6], &EntryType::Directory, "dir1", 0, "2025-01-26 12:54");
assert_entry(&entries[7], &EntryType::File, "debian4", 128_974_848, "2024-12-08 08:22");
assert_entry(&entries[8], &EntryType::Directory, "debian3", 0, "2025-03-01 07:11");
let output = format!("{httpdir}");
assert_eq!(
output,
r##"Processed '' in 0.00ns (0.00ns + 0.00ns)
DIR - ..
DIR - 2025-02-16 13:37 test2
FILE 2345 2023-01-01 00:00 files2
FILE 123 1987-10-09 04:37 file1
DIR - 2025-01-02 12:32 entry4
FILE 67K 2025-07-17 23:59 entry3
DIR - 2025-01-26 12:54 dir1
FILE 123M 2024-12-08 08:22 debian4
DIR - 2025-03-01 07:11 debian3
"##
);
}
#[test]
fn test_httpdirectory_sort_by_date() {
let httpdir = prepare_httpdir().sort_by_date(true);
let entries = httpdir.entries();
assert_entry(&entries[0], &EntryType::ParentDirectory, "../", 0, "0000-00-00 00:00");
assert_entry(&entries[1], &EntryType::File, "file1", 123, "1987-10-09 04:37");
assert_entry(&entries[2], &EntryType::File, "files2", 2_345, "2023-01-01 00:00");
assert_entry(&entries[3], &EntryType::File, "debian4", 128_974_848, "2024-12-08 08:22");
assert_entry(&entries[4], &EntryType::Directory, "entry4", 0, "2025-01-02 12:32");
assert_entry(&entries[5], &EntryType::Directory, "dir1", 0, "2025-01-26 12:54");
assert_entry(&entries[6], &EntryType::Directory, "test2", 0, "2025-02-16 13:37");
assert_entry(&entries[7], &EntryType::Directory, "debian3", 0, "2025-03-01 07:11");
assert_entry(&entries[8], &EntryType::File, "entry3", 68_608, "2025-07-17 23:59");
let output = format!("{httpdir}");
assert_eq!(
output,
r##"Processed '' in 0.00ns (0.00ns + 0.00ns)
DIR - ..
FILE 123 1987-10-09 04:37 file1
FILE 2345 2023-01-01 00:00 files2
FILE 123M 2024-12-08 08:22 debian4
DIR - 2025-01-02 12:32 entry4
DIR - 2025-01-26 12:54 dir1
DIR - 2025-02-16 13:37 test2
DIR - 2025-03-01 07:11 debian3
FILE 67K 2025-07-17 23:59 entry3
"##
);
let httpdir = httpdir.sort_by_date(false);
let entries = httpdir.entries();
assert_entry(&entries[0], &EntryType::ParentDirectory, "../", 0, "0000-00-00 00:00");
assert_entry(&entries[1], &EntryType::File, "entry3", 68_608, "2025-07-17 23:59");
assert_entry(&entries[2], &EntryType::Directory, "debian3", 0, "2025-03-01 07:11");
assert_entry(&entries[3], &EntryType::Directory, "test2", 0, "2025-02-16 13:37");
assert_entry(&entries[4], &EntryType::Directory, "dir1", 0, "2025-01-26 12:54");
assert_entry(&entries[5], &EntryType::Directory, "entry4", 0, "2025-01-02 12:32");
assert_entry(&entries[6], &EntryType::File, "debian4", 128_974_848, "2024-12-08 08:22");
assert_entry(&entries[7], &EntryType::File, "files2", 2_345, "2023-01-01 00:00");
assert_entry(&entries[8], &EntryType::File, "file1", 123, "1987-10-09 04:37");
let output = format!("{httpdir}");
assert_eq!(
output,
r##"Processed '' in 0.00ns (0.00ns + 0.00ns)
DIR - ..
FILE 67K 2025-07-17 23:59 entry3
DIR - 2025-03-01 07:11 debian3
DIR - 2025-02-16 13:37 test2
DIR - 2025-01-26 12:54 dir1
DIR - 2025-01-02 12:32 entry4
FILE 123M 2024-12-08 08:22 debian4
FILE 2345 2023-01-01 00:00 files2
FILE 123 1987-10-09 04:37 file1
"##
);
}
#[test]
fn test_httpdirectory_sort_by_size() {
let httpdir = prepare_httpdir().sort_by_size(true);
let entries = httpdir.entries();
assert_entry(&entries[0], &EntryType::ParentDirectory, "../", 0, "0000-00-00 00:00");
assert_entry(&entries[1], &EntryType::Directory, "dir1", 0, "2025-01-26 12:54");
assert_entry(&entries[2], &EntryType::Directory, "test2", 0, "2025-02-16 13:37");
assert_entry(&entries[3], &EntryType::Directory, "debian3", 0, "2025-03-01 07:11");
assert_entry(&entries[4], &EntryType::Directory, "entry4", 0, "2025-01-02 12:32");
assert_entry(&entries[5], &EntryType::File, "file1", 123, "1987-10-09 04:37");
assert_entry(&entries[6], &EntryType::File, "files2", 2_345, "2023-01-01 00:00");
assert_entry(&entries[7], &EntryType::File, "entry3", 68_608, "2025-07-17 23:59");
assert_entry(&entries[8], &EntryType::File, "debian4", 128_974_848, "2024-12-08 08:22");
let output = format!("{httpdir}");
assert_eq!(
output,
r##"Processed '' in 0.00ns (0.00ns + 0.00ns)
DIR - ..
DIR - 2025-01-26 12:54 dir1
DIR - 2025-02-16 13:37 test2
DIR - 2025-03-01 07:11 debian3
DIR - 2025-01-02 12:32 entry4
FILE 123 1987-10-09 04:37 file1
FILE 2345 2023-01-01 00:00 files2
FILE 67K 2025-07-17 23:59 entry3
FILE 123M 2024-12-08 08:22 debian4
"##
);
let httpdir = httpdir.sort_by_size(false);
let entries = httpdir.entries();
assert_entry(&entries[0], &EntryType::ParentDirectory, "../", 0, "0000-00-00 00:00");
assert_entry(&entries[1], &EntryType::File, "debian4", 128_974_848, "2024-12-08 08:22");
assert_entry(&entries[2], &EntryType::File, "entry3", 68_608, "2025-07-17 23:59");
assert_entry(&entries[3], &EntryType::File, "files2", 2_345, "2023-01-01 00:00");
assert_entry(&entries[4], &EntryType::File, "file1", 123, "1987-10-09 04:37");
assert_entry(&entries[5], &EntryType::Directory, "dir1", 0, "2025-01-26 12:54");
assert_entry(&entries[6], &EntryType::Directory, "test2", 0, "2025-02-16 13:37");
assert_entry(&entries[7], &EntryType::Directory, "debian3", 0, "2025-03-01 07:11");
assert_entry(&entries[8], &EntryType::Directory, "entry4", 0, "2025-01-02 12:32");
let output = format!("{httpdir}");
assert_eq!(
output,
r##"Processed '' in 0.00ns (0.00ns + 0.00ns)
DIR - ..
FILE 123M 2024-12-08 08:22 debian4
FILE 67K 2025-07-17 23:59 entry3
FILE 2345 2023-01-01 00:00 files2
FILE 123 1987-10-09 04:37 file1
DIR - 2025-01-26 12:54 dir1
DIR - 2025-02-16 13:37 test2
DIR - 2025-03-01 07:11 debian3
DIR - 2025-01-02 12:32 entry4
"##
);
}
#[test]
fn test_httpdirectory_stats() {
let httpdir = prepare_httpdir();
let stats = httpdir.stats();
assert_eq!(stats.parent_dir, 1);
assert_eq!(stats.dirs, 4);
assert_eq!(stats.files, 4);
assert_eq!(stats.total_size, 129_045_924);
}
#[test]
fn test_stats_new() {
let stats = Stats::new();
assert_eq!(stats.parent_dir, 0);
assert_eq!(stats.dirs, 0);
assert_eq!(stats.files, 0);
assert_eq!(stats.total_size, 0);
assert_eq!(stats.with_date, 0);
assert_eq!(stats.total_size, 0);
}
#[test]
fn test_stats_count() {
let httpdirectoryentry = HttpDirectoryEntry::new("name", "2025-05-31 16:58", "-", "name/");
let mut httpdirectory = HttpDirectory::default();
httpdirectory.entries = vec![httpdirectoryentry];
let stats = httpdirectory.stats();
assert_eq!(stats.parent_dir, 0);
assert_eq!(stats.dirs, 1);
assert_eq!(stats.files, 0);
assert_eq!(stats.total_size, 0);
assert_eq!(stats.with_date, 1);
assert_eq!(stats.without_date, 0);
let httpdirectoryentry = HttpDirectoryEntry::new("name", "2025-05-31 16:58", "3.1K", "name/");
httpdirectory.entries.push(httpdirectoryentry);
let stats = httpdirectory.stats();
assert_eq!(stats.parent_dir, 0);
assert_eq!(stats.dirs, 1);
assert_eq!(stats.files, 1);
assert_eq!(stats.total_size, 3174);
assert_eq!(stats.with_date, 2);
assert_eq!(stats.without_date, 0);
let httpdirectoryentry = HttpDirectoryEntry::new("Parent Directory", "2025-05-31 16:58", "-", "../");
httpdirectory.entries.push(httpdirectoryentry);
let stats = httpdirectory.stats();
assert_eq!(stats.parent_dir, 1);
assert_eq!(stats.dirs, 1);
assert_eq!(stats.files, 1);
assert_eq!(stats.total_size, 3174);
assert_eq!(stats.with_date, 2);
assert_eq!(stats.without_date, 1);
}
#[test]
fn test_stats_output() {
let httpdirectoryentry = HttpDirectoryEntry::new("name", "2025-05-31 16:58", "3.1K", "name/");
let mut httpdirectory = HttpDirectory::default();
httpdirectory.entries = vec![httpdirectoryentry];
let stats = httpdirectory.stats();
let output = r##"Parent directory: 0
Directories: 0
Files: 1
Total apparent file sizes: 3174
Entries with dates: 1
Entries without any date: 0
"##;
assert_eq!(stats.to_string(), output);
}
#[test]
fn test_stats_count_no_date() {
let httpdirectoryentry = HttpDirectoryEntry::new("name", "", "-", "name/");
let mut httpdirectory = HttpDirectory::default();
httpdirectory.entries = vec![httpdirectoryentry];
let mut stats = httpdirectory.stats();
assert_eq!(stats.parent_dir, 0);
assert_eq!(stats.dirs, 1);
assert_eq!(stats.files, 0);
assert_eq!(stats.total_size, 0);
assert_eq!(stats.with_date, 0);
assert_eq!(stats.without_date, 1);
let httpdirectoryentry = HttpDirectoryEntry::new("name", "", "3.1K", "name/");
httpdirectory.entries.push(httpdirectoryentry);
stats = httpdirectory.stats();
assert_eq!(stats.parent_dir, 0);
assert_eq!(stats.dirs, 1);
assert_eq!(stats.files, 1);
assert_eq!(stats.total_size, 3174);
assert_eq!(stats.with_date, 0);
assert_eq!(stats.without_date, 2);
}
}