use crossbeam_queue::SegQueue;
use extra::try_send_message;
use processes::{process, process_with_sender};
use std::error::Error;
use std::sync::mpsc::Sender;
use std::sync::Arc;
use std::{thread, io};
use std::path::{Path, PathBuf};
use std::fs::create_dir_all;
mod core;
mod extra;
mod processes;
pub use extra::{get_dir_list, get_dir_list_with_depth};
pub struct Archiver {
dest: Option<PathBuf>,
thread_count: u32,
sender: Option<Sender<String>>,
queue: Option<SegQueue<PathBuf>>,
}
impl Archiver {
pub fn new() -> Self{
Archiver {
dest: None,
thread_count: 1,
sender: None,
queue: None
}
}
pub fn set_destination<T: AsRef<Path>>(&mut self, dest: T){
self.dest = Some(dest.as_ref().to_path_buf());
}
pub fn set_thread_count(&mut self, thread_count: u32){
self.thread_count = thread_count;
}
pub fn set_sender(&mut self, sender: Sender<String>){
self.sender = Some(sender);
}
pub fn push_from_iter<I>(&mut self, iter: I)
where
I: Iterator,
I::Item: AsRef<Path>,
{
if let None = self.queue{
self.queue = Some(SegQueue::new());
}
for i in iter{
self.queue.as_ref().unwrap().push(i.as_ref().to_path_buf());
}
}
pub fn push<T: AsRef<Path>>(&mut self, path: T){
if let None = self.queue {
self.queue = Some(SegQueue::new());
}
self.queue.as_ref().unwrap().push(path.as_ref().to_path_buf());
}
pub fn archive(&self) -> Result<(), Box<dyn Error>> {
match &self.dest{
Some(p) if !p.is_dir() => create_dir_all(p)?,
None => return Err(Box::new(io::Error::new(io::ErrorKind::NotFound, "Destination directory is not set"))),
_ => (),
};
match &self.queue {
Some(q) => {
try_send_message(&self.sender,
format!(
"Total archive directory count: {}",
q.len()
));
},
None => {
try_send_message(&self.sender, "There are no files to archive in queue.".to_string());
return Ok(());
},
}
let queue = Arc::new(Archiver::copy_queue(self.queue.as_ref().unwrap()));
let dest = Arc::new(self.dest.clone().unwrap());
let mut handles = Vec::new();
for _ in 0..self.thread_count {
let arc_queue = Arc::clone(&queue);
let arc_dest = Arc::clone(&dest);
let handle;
match self.sender{
Some(ref s) => {
let new_sender = s.clone();
handle = thread::spawn(move || {
process_with_sender(arc_queue, &arc_dest, new_sender);
});
},
None => {
handle = thread::spawn(move || {
process(arc_queue, &arc_dest);
});
},
}
handles.push(handle);
}
for h in handles {
h.join().unwrap();
}
try_send_message(&self.sender, "Archiving Complete!".to_string());
Ok(())
}
fn copy_queue<T>(queue: &SegQueue<T>) -> SegQueue<T>{
let new_queue = SegQueue::new();
while !queue.is_empty() {
new_queue.push(queue.pop().unwrap());
}
new_queue
}
}
#[cfg(test)]
mod tests{
use super::*;
use std::sync::mpsc;
use crate::core::test_util::setup;
#[test]
fn archive_root_dir_test() {
let (origin, dest) = setup();
let mut archiver = Archiver::new();
archiver.push_from_iter(get_dir_list(origin).unwrap().iter());
archiver.set_destination(dest);
archiver.archive().unwrap();
}
#[test]
fn archive_root_dir_with_sender_test() {
let (origin, dest) = setup();
let (tx, tr) = mpsc::channel();
{
let mut archiver = Archiver::new();
archiver.push_from_iter(get_dir_list(origin).unwrap().iter());
archiver.set_destination(dest);
archiver.set_sender(tx);
archiver.archive().unwrap();
}
for re in tr {
println!("{}", re);
}
}
#[test]
fn copy_queue_test(){
let queue1 = SegQueue::new();
queue1.push("value1");
queue1.push("value2");
queue1.push("value3");
queue1.push("value4");
let queue2 = Archiver::copy_queue(&queue1);
while !queue2.is_empty() {
println!("{}", queue2.pop().unwrap());
}
}
#[test]
fn add_queue_test(){
let (origin, dest) = setup();
let queue = SegQueue::new();
for dir in origin.read_dir().unwrap(){
queue.push(dir.unwrap().path().to_path_buf());
}
let mut archiver = Archiver::new();
archiver.push_from_iter(queue.into_iter());
archiver.set_destination(dest.to_path_buf());
archiver.archive().unwrap();
assert!(dest.join("dir1.7z").is_file());
assert!(dest.join("dir2.7z").is_file());
assert!(dest.join("dir3.7z").is_file());
let (origin, dest) = setup();
let files = get_dir_list(origin).unwrap();
let mut archiver = Archiver::new();
archiver.push_from_iter(files.iter());
archiver.set_destination(dest.to_path_buf());
archiver.archive().unwrap();
assert!(dest.join("dir1.7z").is_file());
assert!(dest.join("dir2.7z").is_file());
assert!(dest.join("dir3.7z").is_file());
let (origin, dest) = setup();
let mut archiver = Archiver::new();
archiver.push_from_iter(vec![
origin.join("dir1").to_str().unwrap(),
origin.join("dir2").to_str().unwrap(),
origin.join("dir3").to_str().unwrap()
].into_iter());
archiver.set_destination(dest.to_path_buf());
archiver.archive().unwrap();
assert!(dest.join("dir1.7z").is_file());
assert!(dest.join("dir2.7z").is_file());
assert!(dest.join("dir3.7z").is_file());
}
#[test]
fn push_test(){
let (origin, dest) = setup();
let mut archiver = Archiver::new();
archiver.push(origin.join("dir1"));
archiver.push(origin.join("dir2"));
archiver.set_destination(dest);
archiver.archive().unwrap();
}
}