use std::collections::HashMap;
use std::str::FromStr;
use clap::{crate_authors, crate_version, App, Arg};
use strum::AsStaticRef;
use strum_macros::AsStaticStr;
use crate::copier::Copier;
use crate::strats::build_destination::PrefixStrippedCountParentStrat;
use crate::strats::copy::{FileHardlinkStrat, FileSymlinkStrat};
#[derive(AsStaticStr, Eq, PartialOrd, PartialEq, Hash)]
pub enum OptNames {
#[strum(serialize = "link")]
Link,
#[strum(serialize = "paths")]
Paths,
#[strum(serialize = "parents-stripped")]
ParentsStripped,
#[strum(serialize = "symbolic-link")]
Symbolic,
}
pub struct AppOpt<'a> {
pub arg: Arg<'a, 'a>,
pub copier_modifier: Box<dyn Fn(&mut Copier, Option<&str>)>,
}
fn validate_positive_number(value: String) -> Result<(), String> {
let result = usize::from_str(value.as_str());
if result.is_ok() {
Ok(())
} else {
Err(String::from("Must be a positive number"))
}
}
pub fn get_opts() -> HashMap<OptNames, AppOpt<'static>> {
let mut hashmap = HashMap::new();
hashmap.insert(
OptNames::Link,
AppOpt {
arg: Arg::with_name(OptNames::Link.as_static())
.long(OptNames::Link.as_static())
.short("l")
.help("Make hard links instead of copying"),
copier_modifier: Box::new(|copier, _| {
copier.copy_strategy = Box::new(FileHardlinkStrat {});
}),
},
);
hashmap.insert(
OptNames::Symbolic,
AppOpt {
arg: Arg::with_name(OptNames::Symbolic.as_static())
.long(OptNames::Symbolic.as_static())
.short("s")
.help("Make symbolic links instead of copying"),
copier_modifier: Box::new(|copier, _| {
copier.copy_strategy = Box::new(FileSymlinkStrat {});
}),
},
);
hashmap.insert(
OptNames::ParentsStripped,
AppOpt {
arg: Arg::with_name(OptNames::ParentsStripped.as_static())
.long(OptNames::ParentsStripped.as_static())
.takes_value(true)
.validator(validate_positive_number)
.help(
"Remove X components from the parent and tack onto destination (use as prefix)",
),
copier_modifier: Box::new(|copier, value_opt| {
let count = value_opt
.and_then(|value| usize::from_str(value).ok())
.expect("Must be a positive number");
copier.build_destination_strategy =
Box::new(PrefixStrippedCountParentStrat { count });
}),
},
);
hashmap
}
pub fn get_app<'a, 'b>(opts: &HashMap<OptNames, AppOpt<'a>>) -> App<'a, 'b> {
let mut app = App::new("kpy")
.version(crate_version!())
.author(crate_authors!())
.about("An alternative to cp")
.arg(
Arg::with_name(OptNames::Paths.as_static())
.help("FILE DIRECTORY | DIRECTORY DIRECTORY | FILE1 FILE2 ... DIRECTORY")
.required(true)
.index(1)
.multiple(true)
.min_values(2),
);
for app_opt in opts.values().into_iter() {
app = app.arg(app_opt.arg.clone());
}
app
}