use std::fs;
use std::path::{Path, PathBuf};
use anyhow::{Result, Context};
#[derive(Debug, Clone)]
pub struct FileInfo {
pub path: PathBuf,
pub name: String,
pub size: u64,
pub modified: std::time::SystemTime,
}
pub fn scan_downloads(download_dir: &Path) -> Result<Vec<FileInfo>> {
if !download_dir.exists() {
anyhow::bail!("Downloads directory not found: {:?}", download_dir);
}
if !download_dir.is_dir() {
anyhow::bail!("Specified path is not a directory: {:?}", download_dir);
}
let mut files = Vec::new();
for entry in fs::read_dir(download_dir)
.context("Failed to read downloads directory")?
{
let entry = entry.context("Failed to read directory entry")?;
let path = entry.path();
if !path.is_dir() {
let metadata = fs::metadata(&path)
.context(format!("Failed to read file metadata: {:?}", path))?;
let name = path
.file_name()
.and_then(|n| n.to_str())
.map(|s| s.to_string())
.unwrap_or_else(|| "unknown".to_string());
files.push(FileInfo {
path: path.clone(),
name,
size: metadata.len(),
modified: metadata.modified()?,
});
}
}
files.sort_by(|a, b| a.name.cmp(&b.name));
Ok(files)
}
pub fn display_scan_results(files: &[FileInfo]) {
if files.is_empty() {
println!("No files found.");
return;
}
println!("Scan results: {} file(s) found\n", files.len());
println!("{:<50} {:<15} {:<20}", "Filename", "Size (B)", "Modified");
println!("{}", "-".repeat(85));
let mut total_size = 0u64;
for file in files {
let size_str = format!("{}", file.size);
let modified_str = match file.modified.duration_since(std::time::UNIX_EPOCH) {
Ok(duration) => {
let secs = duration.as_secs();
let datetime = chrono::DateTime::<chrono::Local>::from(
std::time::UNIX_EPOCH + std::time::Duration::from_secs(secs)
);
datetime.format("%Y-%m-%d %H:%M:%S").to_string()
}
Err(_) => "Unknown".to_string(),
};
println!("{:<50} {:<15} {:<20}",
&file.name[..file.name.len().min(50)],
size_str,
modified_str
);
total_size += file.size;
}
println!("{}", "-".repeat(85));
println!("Total size: {} B ({:.2} MB)", total_size, total_size as f64 / 1024.0 / 1024.0);
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_scan_downloads_empty_directory() {
let temp_dir = TempDir::new().unwrap();
let files = scan_downloads(temp_dir.path()).unwrap();
assert_eq!(files.len(), 0);
}
#[test]
fn test_scan_downloads_with_files() {
let temp_dir = TempDir::new().unwrap();
let test_file1 = temp_dir.path().join("test1.txt");
let test_file2 = temp_dir.path().join("test2.txt");
fs::write(&test_file1, "content1").unwrap();
fs::write(&test_file2, "content2").unwrap();
let files = scan_downloads(temp_dir.path()).unwrap();
assert_eq!(files.len(), 2);
assert_eq!(files[0].name, "test1.txt");
assert_eq!(files[1].name, "test2.txt");
}
}