use chrono::{DateTime, Utc};
use std::{fs, fmt};
use std::ops::Not;
use std::error::Error;
use std::fs::Metadata;
use std::path::PathBuf;
use std::io;
use std::fmt::Formatter;
struct DirInfo {
metadata: Metadata,
path_buf: PathBuf,
}
#[derive(Debug,Clone)]
struct ArgsError;
impl Error for ArgsError {}
impl fmt::Display for ArgsError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Insufficient arguments provided.\n Usage: `low_level_four |relative_path_to_folder|`")
}
}
pub fn run(mut args: impl Iterator<Item=String>) -> Result<(), Box<dyn Error>> {
args.next();
let directory = match args.next() {
Some(str) => str,
None => return Err(ArgsError.into()),
};
let input = get_input(
"Please, provide the name of the file you want to search (including its file extension)",
);
let mut files_info = get_dir_files(&directory, input.trim())?;
for (i, file) in files_info.iter().enumerate() {
println!("Entry {}", &i + 1);
file.show_info();
}
let answer = get_input("Do you want to keep every file? \n(y/n)");
if answer.trim().eq("y") {
println!("Good Bye!");
return Ok(());
}
loop {
let answer = get_input(
"Please provide the number associated to the file you want to delete.\nWrite done to quit",
);
let cleaned_answer = answer.trim();
if cleaned_answer.eq("done") {
println!("Good Bye!");
break;
}
let index = cleaned_answer.parse::<usize>();
let index = match index {
Ok(index) => index,
Err(_) => {
println!("Invalid number provided.");
continue;
}
};
if (index >= *&files_info.len() + 1) || (index <= 0) {
println!("Please provide one of the listed numbers!");
break;
}
let file = &files_info.swap_remove(&index - 1);
file.delete()?;
println!("File deleted!");
}
Ok(())
}
#[derive(PartialEq, Debug)]
pub struct File {
pub name: String,
pub folder: String,
pub creation_date: String,
path: String,
}
impl File {
pub fn new(name: &str, folder: &str, creation_date: &str, path: &str) -> Self {
Self {
name: name.to_string(),
folder: folder.to_string(),
creation_date: creation_date.to_string(),
path: path.to_string(),
}
}
pub fn show_info(&self) {
println!(
"\tfile name: {} \n\tdirectory: {} \n\tcreation date: {}",
&self.name, &self.folder, &self.creation_date
);
}
pub fn delete(&self) -> Result<(), std::io::Error> {
fs::remove_file(&self.path)?;
Ok(())
}
}
pub fn get_dir_files(path: &str, file_name: &str) -> Result<Vec<File>, std::io::Error> {
let dir_entry = fs::read_dir(&path)?;
let mut sub_dirs: Vec<String> = vec![];
let mut files: Vec<File> = dir_entry.filter(|f| f.is_ok())
.flatten()
.map(|d| {
let path = &d.path();
let metadata = &d.metadata().unwrap();
DirInfo {
path_buf: path.to_owned(),
metadata: metadata.to_owned(),
}
})
.filter(|fi| {
if fi.path_buf.is_file().not() {
sub_dirs.push(fi.path_buf.to_str().unwrap().to_string());
}
fi.path_buf.is_file()
})
.map(|fi| {
let file_path = &fi.path_buf.to_str().unwrap();
let entry_name = &fi.path_buf.file_name().unwrap();
let entry_name = entry_name.to_str().unwrap();
let creation_date = fi.metadata.created().unwrap();
let creation_date: DateTime<Utc> = creation_date.clone().into();
let creation_date = creation_date.format("%Y-%m-%d %H:%M:%S").to_string();
File::new(entry_name, &path, &creation_date, file_path)
})
.filter(|f| f.name.eq(file_name))
.collect();
for sub_dir in sub_dirs {
let mut sub_files = get_dir_files(&sub_dir, file_name)?;
files.append(&mut sub_files);
}
Ok(files)
}
pub fn get_input(message: &str) -> String {
println!("{}", message);
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Couldn't read the provided information.");
input
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn file_constructor() {
let name= "test.txt";
let folder= ".";
let creation_date= "2022-07-23 12:33:01";
let path= "./test.txt";
let file = File {
name: name.to_owned(),
folder: folder.to_owned(),
creation_date: creation_date.to_owned(),
path: path.to_owned(),
};
assert_eq!(file, File::new(name, folder, creation_date, path));
}
#[test]
fn file_delete() -> Result<(), std::io::Error> {
let f = std::fs::File::create("./test.txt").unwrap();
let name = "test.txt";
let path = "./test.txt";
let metadata = &f.metadata().unwrap();
let creation_date = metadata.created().unwrap();
let creation_date: DateTime<Utc> = creation_date.clone().into();
let creation_date = creation_date.format("%Y-%m-%d %H:%M:%S").to_string();
let file = File::new(name, ".", &creation_date, path);
let result = file.delete();
result
}
#[test]
fn get_dir_files() -> Result<(), std::io::Error> {
let f = std::fs::File::create("./text.txt").unwrap();
let name = "text.txt";
let path = std::path::Path::new("./").join(name);
let path = path.to_str().unwrap();
let metadata = &f.metadata().unwrap();
let creation_date = metadata.created().unwrap();
let creation_date: DateTime<Utc> = creation_date.clone().into();
let creation_date = creation_date.format("%Y-%m-%d %H:%M:%S").to_string();
let expected_file = File::new(name, "./", &creation_date, &path);
let files = super::get_dir_files("./", "text.txt").unwrap_or_else(|err| {
eprintln!("{}", err);
std::process::exit(1);
});
assert_eq!(vec![expected_file], files);
files[0].delete()
}
}