1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
use clap::{App, Arg}; use serde::{de::DeserializeOwned, Serialize}; use std::{cmp::Eq, collections::HashMap, error::Error, fmt::Debug, hash::Hash, path::Path}; use crate::{Cpt, StringOrVecString}; const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); pub trait FromArgs: Default { fn from_args(defaults: Option<&Self>) -> Result<Self, Box<dyn Error>>; } impl<K, V, S> FromArgs for Cpt<K, V, S> where K: Hash + Eq + DeserializeOwned + Serialize + Debug, V: Hash + Eq + DeserializeOwned + Serialize + Debug + Into<StringOrVecString>, S: std::hash::BuildHasher + Default + Debug, { #[cfg_attr(tarpaulin, skip)] fn from_args(defaults: Option<&Cpt<K, V, S>>) -> Result<Cpt<K, V, S>, Box<dyn Error>> { let plain_def = Self::default(); let def = defaults.unwrap_or(&plain_def); let m = App::new("cpt") .version(VERSION.unwrap_or("unknown")) .about( "Copies one folder structure to another place with files. Also formats templates!", ) .author("AlexAegis") .arg( Arg::with_name("from") .short("f") .long("from") .required(true) .index(1) .default_value(&def.from) .validator(|s| { if Path::new(&s).exists() { Ok(()) } else { Err("Source folder not exists".to_string()) } }) .help("The folder that will be copied"), ) .arg( Arg::with_name("to") .short("t") .long("to") .required(true) .index(2) .default_value(&def.to) .help("The folder where the folder will be placed"), ) .arg( Arg::with_name("json") .short("-j") .long("--json") .takes_value(true) .validator(|s| match serde_json::from_str::<HashMap<K, V>>(&s) { Ok(_) => Ok(()), Err(e) => Err(e.to_string()), }) .help("JSON formatted templating data"), ) .arg( Arg::with_name("dry") .short("-d") .long("--dry") .help("If set, nothing will be written to the disk"), ) .arg( Arg::with_name("force") .short("-f") .long("--force") .help("If set, files can be overwritten in the target folder"), ) .arg( Arg::with_name("quiet") .short("-q") .long("--quiet") .help("Tarpaulin"), ) .get_matches(); let from = m.args["from"] .vals .first() .ok_or("No from specified")? .to_str() .ok_or("Invalid string")?; let to = m.args["to"] .vals .first() .ok_or("No to specified")? .to_str() .ok_or("Invalid string")?; let dry = m.args.get("dry").is_some(); let force = m.args.get("force").is_some(); let mut data_map = None; if m.args.contains_key("json") { if let Some(d) = m.args["json"].vals.first() { let data_str = d.to_str().ok_or("Invalid string")?; data_map.replace(Box::from(serde_json::from_str::<HashMap<K, V, S>>( &data_str, )?)); } } Ok(Cpt::new(from.to_string(), to.to_string()) .try_data(data_map) .set_dry(dry) .set_force(force)) } }