use std::collections::HashMap;
use std::error::Error;
use std::fs::create_dir_all;
use std::io;
use std::io::ErrorKind;
use std::path::Path;
use clap::ArgMatches;
use log::debug;
use strum::AsStaticRef;
use walkdir::WalkDir;
use crate::app::{AppOpt, OptNames};
use crate::error::BaseError;
use crate::strats::build_destination::{BuildDestinationStratTrait, NoopDestinationStrat};
use crate::strats::copy::{CopyStratTrait, FileCopyStrat};
use crate::strats::prep_destination::{CreateParentDirStrat, PrepDestinationStratTrait};
pub fn handle_args(
matches: &ArgMatches,
opts: &HashMap<OptNames, AppOpt>,
) -> Result<(), Box<dyn Error>> {
let paths: Vec<&str> = matches
.values_of(OptNames::Paths.as_static())
.ok_or("No paths?")?
.collect();
let (destination, sources) = paths
.split_last()
.ok_or("Couldn't split sources from destination")?;
let copier = Copier::new(matches, opts);
let destination_path = Path::new(destination);
for source in sources.iter() {
let source_path = Path::new(source);
copier.run(source_path, destination_path)?;
}
Ok(())
}
pub struct Copier {
pub copy_strategy: Box<dyn CopyStratTrait>,
pub pre_destination_strategy: Box<dyn PrepDestinationStratTrait>,
pub build_destination_strategy: Box<dyn BuildDestinationStratTrait>,
}
impl Copier {
pub fn new(matches: &ArgMatches, opts: &HashMap<OptNames, AppOpt>) -> Self {
let mut copier = Copier {
copy_strategy: Box::new(FileCopyStrat {}),
pre_destination_strategy: Box::new(CreateParentDirStrat {}),
build_destination_strategy: Box::new(NoopDestinationStrat {}),
};
for (opt_name, app_opt) in opts.iter() {
let opt_str = opt_name.as_static();
debug!("Checking for {} option", opt_str);
if matches.is_present(opt_str) {
(app_opt.copier_modifier)(&mut copier, matches.value_of(opt_str));
debug!("Applied option {}", opt_name.as_static())
}
}
copier
}
pub fn run(&self, source: &Path, destination: &Path) -> Result<(), Box<dyn Error>> {
if !source.exists() {
throw!("Source must exist")
} else if source.is_file() {
let dest_buf = self.build_destination_strategy.build(source, destination)?;
let actual_destination = dest_buf.as_path();
self.pre_destination_strategy.prep(actual_destination)?;
self.copy_strategy.run(source, actual_destination)?;
} else if source.is_dir() {
let dest_root = self.build_destination_strategy.build(source, destination)?;
for entry in WalkDir::new(source)
.into_iter()
.into_iter()
.filter_map(|e| e.ok())
{
let entry_path = entry.path();
let rel_source = entry_path.strip_prefix(source).map_err(|e| {
io::Error::new(ErrorKind::Other, format!("Could not strip prefix: {:?}", e))
})?;
let entry_destination = dest_root.join(rel_source);
if entry_path.is_file() {
self.copy_strategy
.run(entry_path, entry_destination.as_path())?;
} else {
create_dir_all(entry_destination)?;
}
}
} else {
throw_fmt!("Cannot handle source {}", source.display())
}
Ok(())
}
}