use std::collections::HashMap;
use std::ffi::OsString;
use std::fs::{create_dir_all, rename};
use std::io;
use std::path::PathBuf;
use time::macros::format_description;
use super::tools::collect_files::collect_files;
use super::tools::get_num_files::get_num_files;
use super::RunTask;
#[derive(Debug, PartialEq, Eq)]
pub struct OrganizeTask {
source: PathBuf,
target: PathBuf,
}
impl RunTask for OrganizeTask {
fn run_task(&self) -> Result<(), io::Error> {
let mut file_vec: Vec<PathBuf> = Vec::new();
let files = collect_files(&self.source, &mut file_vec)?.iter();
let mut count_cache: HashMap<String, usize> = HashMap::new();
let mut count: usize;
for file in files {
let c_date: time::OffsetDateTime = file.metadata()?.modified()?.into();
let fc_date = PathBuf::from(
c_date
.format(&format_description!("[year]/[year]-[month]"))
.unwrap(),
);
let mut target: PathBuf = [&self.target, &fc_date].iter().collect();
let key = &target.to_str().unwrap().to_string();
if count_cache.contains_key(key) {
count_cache
.entry(key.clone())
.and_modify(|count| *count += 1);
} else {
if target.exists() {
count = get_num_files(&target)? + 1;
} else {
create_dir_all(&target)?;
count = 1;
}
count_cache.insert(key.clone(), count);
}
target.push(format!(
"{}_{}.{}",
&key[key.len() - 7..],
count_cache.get(&key.clone()).unwrap() - 1, file.extension()
.unwrap_or(&OsString::from("")) .to_str()
.unwrap()
.to_string()
));
rename(file, target)?;
}
Ok(())
}
}
impl OrganizeTask {
pub fn new(mut args: impl Iterator<Item = String>) -> Result<Self, &'static str> {
let source = match args.next() {
Some(arg) => PathBuf::from(arg),
None => return Err("no 'source' path provided"),
};
if !source.is_dir() {
return Err("'source' path does not correspond to a valid directory");
}
let target = match args.next() {
Some(arg) => PathBuf::from(arg),
None => return Err("no 'target' path provided"),
};
if !target.is_dir() {
return Err("'target' path does not correspond to a valid directory");
}
Ok(Self { source, target })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn organize_task_new_with_valid_args() {
let mut args = [
String::from("foo"),
String::from("bar"),
String::from("./src"),
String::from("./src/organize"),
]
.into_iter();
args.next();
args.next();
let source = PathBuf::from("./src");
let target = PathBuf::from("./src/organize");
assert_eq!(OrganizeTask::new(args), Ok(OrganizeTask { source, target }));
}
#[test]
fn organize_task_new_source_not_provided() {
let mut args = [String::from("foo"), String::from("bar")].into_iter();
args.next();
args.next();
assert!(OrganizeTask::new(args).is_err())
}
#[test]
fn organize_task_new_source_is_not_dir() {
let mut args = [
String::from("foo"),
String::from("bar"),
String::from("not_a_dir"),
String::from("./src/organize"),
]
.into_iter();
args.next();
args.next();
assert!(OrganizeTask::new(args).is_err())
}
#[test]
fn organize_task_new_target_not_provided() {
let mut args = [
String::from("foo"),
String::from("bar"),
String::from("not_a_dir"),
]
.into_iter();
args.next();
args.next();
assert!(OrganizeTask::new(args).is_err())
}
#[test]
fn organize_task_new_target_is_not_dir() {
let mut args = [
String::from("foo"),
String::from("bar"),
String::from("./src"),
String::from("not_a_dir"),
]
.into_iter();
args.next();
args.next();
assert!(OrganizeTask::new(args).is_err())
}
}