diff options
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/main.rs | 193 | ||||
-rw-r--r-- | tests/tests.rs | 49 |
3 files changed, 162 insertions, 81 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..2a04341 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod main; diff --git a/src/main.rs b/src/main.rs index cd10462..bb1701a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ extern crate dirs; use clap::{App, AppSettings::ArgRequiredElseHelp, Arg}; use csv::{ReaderBuilder, Writer}; use std::fs; +use std::io; use std::path::{Component, PathBuf}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -37,102 +38,116 @@ fn main() { fs::create_dir(&rsyclebin).unwrap(); } - if matches.is_present("restore") { - if let Some(filename) = matches.value_of("INPUT") { - let filename = filename.to_owned() + "."; - restore(rsyclebin, &filename); + if let Some(filename) = matches.value_of("INPUT") { + let path = build_path(filename).unwrap(); + if matches.is_present("restore") { + restore(rsyclebin, path).unwrap(); } else { - restore_cli(rsyclebin); + rsycle(rsyclebin, path).unwrap(); } } else if matches.is_present("empty") { - empty(rsyclebin); + empty(rsyclebin).unwrap(); } else if matches.is_present("list") { - list(rsyclebin); - } else if let Some(filename) = matches.value_of("INPUT") { - rsycle(rsyclebin, filename); + list(rsyclebin).unwrap(); + } else if matches.is_present("restore") { + restore_cli(rsyclebin); } } -fn log(rsyclebin: PathBuf, old_path: PathBuf, new_path: PathBuf) { +pub fn build_path(filename: &str) -> Result<PathBuf, io::Error> { + let relative: PathBuf = [Component::CurDir, Component::Normal(filename.as_ref())] + .iter() + .collect(); + + fs::canonicalize(&relative) +} + +pub fn rsycle(rsyclebin: PathBuf, old_path: PathBuf) -> Result<(), io::Error> { + if !old_path.exists() { + Err(io::Error::new(io::ErrorKind::NotFound, "File not found!")) + } else { + let new_filename = old_path.file_name().unwrap().to_str().unwrap().to_owned() + + "." + + &SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + .to_string(); + + let mut new_path = rsyclebin.clone(); + new_path.push(new_filename); + + log(rsyclebin, old_path.clone(), new_path.clone())?; + fs::rename(old_path, new_path) + } +} + +fn log(rsyclebin: PathBuf, old_path: PathBuf, new_path: PathBuf) -> Result<(), io::Error> { let mut rsyclebin_log = rsyclebin.clone(); rsyclebin_log.push(".log"); let file = fs::OpenOptions::new() .append(true) .create(true) - .open(rsyclebin_log) - .unwrap(); + .open(rsyclebin_log)?; let mut writer = Writer::from_writer(file); - writer - .write_record(&[ - fs::canonicalize(old_path).unwrap().to_str().unwrap(), - new_path.to_str().unwrap(), - ]) - .unwrap(); - writer.flush().unwrap(); + writer.write_record(&[ + fs::canonicalize(old_path)?.to_str().unwrap(), + new_path.to_str().unwrap(), + ])?; + writer.flush() } -fn rsycle(rsyclebin: PathBuf, filename: &str) { - let old_path: PathBuf = [Component::CurDir, Component::Normal(filename.as_ref())] - .iter() - .collect(); +pub fn restore(rsyclebin: PathBuf, original_path: PathBuf) -> Result<(), io::Error> { + let current_path = most_recent_current_path(rsyclebin, original_path.clone())?; - if !old_path.exists() { - println!("File not found!"); - return; + if !original_path.exists() { + fs::rename(current_path, original_path) + } else { + Err(io::Error::new( + io::ErrorKind::NotFound, + "There is a file in the way!", + )) } +} - let new_filename = filename.to_owned() - + "." - + &SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs() - .to_string(); +pub fn most_recent_current_path(rsyclebin: PathBuf, path: PathBuf) -> Result<PathBuf, io::Error> { + let mut rsyclebin_log = rsyclebin.clone(); + rsyclebin_log.push(".log"); - let mut new_path = rsyclebin.clone(); - new_path.push(new_filename); + let file = fs::OpenOptions::new().read(true).open(rsyclebin_log)?; - log(rsyclebin, old_path.clone(), new_path.clone()); - fs::rename(old_path, new_path).unwrap(); -} + let mut reader = ReaderBuilder::new().has_headers(false).from_reader(file); -fn restore(rsyclebin: PathBuf, filename: &str) { - let paths: Vec<PathBuf> = fs::read_dir(rsyclebin.clone()) - .unwrap() + let path_str = path.to_str().unwrap(); + + let original_paths: Vec<PathBuf> = reader + .records() .map(Result::unwrap) - .map(|dir| dir.path()) - .filter(|path| { + .filter(|line| line.get(0).unwrap() == path_str) + .map(|line| PathBuf::from(line.get(1).unwrap())) + .collect(); + + Ok(original_paths + .iter() + .max_by_key(|path| { path.file_name() .unwrap() .to_str() .unwrap() - .starts_with(filename) + .split('.') + .collect::<Vec<&str>>() + .pop() + .unwrap() + .parse::<u64>() + .unwrap() }) - .collect(); - - match paths.iter().max_by_key(|path| { - path.file_name() - .unwrap() - .to_str() - .unwrap() - .split('.') - .collect::<Vec<&str>>() - .pop() - .unwrap() - .parse::<u64>() - .unwrap() - }) { - Some(latest_path) => { - let original_path = find_original_path(rsyclebin, latest_path.to_path_buf()); - fs::rename(latest_path, original_path).unwrap(); - } - None => println!("No file found !"), - } + .unwrap() + .to_path_buf()) } -fn list(rsyclebin: PathBuf) { +pub fn list(rsyclebin: PathBuf) -> Result<(), io::Error> { let mut paths: Vec<PathBuf> = fs::read_dir(rsyclebin.clone()) .unwrap() .map(Result::unwrap) @@ -150,37 +165,53 @@ fn list(rsyclebin: PathBuf) { if let Some(last_point) = filename.pop() { if let Ok(date) = last_point.parse::<u64>() { - let original_path = find_original_path(rsyclebin.clone(), current_path.clone()); + let original_path = find_original_path(rsyclebin.clone(), current_path.clone())?; let date = UNIX_EPOCH + Duration::from_secs(date); - println!("{:?}, {:?}, {:?}", original_path, current_path, date); + println!( + "original: {:?}, current: {:?}, date: {:?}", + original_path, current_path, date + ); } } } + + Ok(()) } -fn find_original_path(rsyclebin: PathBuf, path: PathBuf) -> String { +fn find_original_path(rsyclebin: PathBuf, current_path: PathBuf) -> Result<PathBuf, io::Error> { let mut rsyclebin_log = rsyclebin.clone(); rsyclebin_log.push(".log"); - let file = fs::OpenOptions::new() - .read(true) - .open(rsyclebin_log) - .unwrap(); + let file = fs::OpenOptions::new().read(true).open(rsyclebin_log)?; let mut reader = ReaderBuilder::new().has_headers(false).from_reader(file); - let path_str = path.to_str().unwrap(); + let path_str = current_path.to_str().unwrap(); - reader - .records() - .map(Result::unwrap) - .find(|line| line.get(1).unwrap() == path_str) - .map(|line| line.get(0).unwrap().to_string()) - .unwrap() + Ok(PathBuf::from( + reader + .records() + .map(Result::unwrap) + .find(|line| line.get(1).unwrap() == path_str) + .map(|line| line.get(0).unwrap().to_string()) + .unwrap(), + )) } -fn empty(rsyclebin: PathBuf) { - fs::remove_dir_all(rsyclebin).unwrap() +pub fn empty(rsyclebin: PathBuf) -> Result<(), io::Error> { + let paths: Vec<PathBuf> = fs::read_dir(rsyclebin.clone()) + .unwrap() + .map(Result::unwrap) + .map(|dir| dir.path()) + .collect(); + + for path in paths { + if fs::remove_file(path.clone()).is_err() { + fs::remove_dir_all(path)? + } + } + + Ok(()) } fn restore_cli(_rsyclebin: PathBuf) { diff --git a/tests/tests.rs b/tests/tests.rs new file mode 100644 index 0000000..efdb909 --- /dev/null +++ b/tests/tests.rs @@ -0,0 +1,49 @@ +extern crate rsycle; + +#[cfg(test)] +mod tests { + use rsycle::main::{build_path, empty, list, restore, rsycle}; + use std::fs; + use std::path::{Component, PathBuf}; + + fn get_rsyclebin() -> PathBuf { + let mut rsyclebin = dirs::home_dir().unwrap(); + rsyclebin.push(".test_rsyclebin"); + if !rsyclebin.exists() { + fs::create_dir(&rsyclebin).unwrap(); + } + rsyclebin + } + + #[test] + fn test_rsycle() { + let rsyclebin = get_rsyclebin(); + + let filename = "test_file".as_ref(); + + let mut test_path: PathBuf = [Component::CurDir, Component::Normal(filename)] + .iter() + .collect(); + + assert!(!test_path.exists()); + + fs::File::create(test_path.clone()).unwrap(); + + test_path = build_path(filename.to_str().unwrap()).unwrap(); + + assert!(rsycle(rsyclebin.clone(), test_path.clone()).is_ok()); + + assert!(!test_path.exists()); + + assert!(list(rsyclebin.clone()).is_ok()); + + assert!(restore(rsyclebin.clone(), test_path.clone()).is_ok()); + + assert!(test_path.exists()); + + assert!(empty(rsyclebin.clone()).is_ok()); + + fs::remove_file(test_path).unwrap(); + fs::remove_dir_all(rsyclebin).unwrap(); + } +} |