diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9f533b9 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,203 @@ +use std::str::FromStr; + +use formula::Formula; + +pub mod formula; + +pub struct Scurvy { + free: Vec<String>, + pairs: Vec<Pair>, + unknown: Vec<(String, String)>, + print_help: bool, + print_version: bool, +} + +struct Pair { + key: Argument, + values: Vec<String>, +} + +impl Scurvy { + pub fn make(args: Vec<Argument>) -> Self { + let mut free = vec![]; + let mut pairs: Vec<Pair> = args.into_iter().map(|a| a.into()).collect(); + let mut unknown = vec![]; + + let mut print_help = false; + let mut print_version = false; + + for arg in std::env::args().skip(1) { + if arg == "-h" || arg == "--help" || arg == "help=" { + print_help = true; + } else if arg == "-V" || arg == "--version" || arg == "version=" { + print_version = true; + } + + match arg.split_once('=') { + None => free.push(arg), + Some((key, value)) => { + if let Some(pair) = pairs.iter_mut().find(|p| p.key.matches(key)) { + pair.values.push(value.to_owned()); + } else { + unknown.push((key.to_owned(), value.to_owned())); + } + } + } + } + + Self { + free, + pairs, + unknown, + print_help, + print_version, + } + } + + pub fn should_print_help(&self) -> bool { + self.print_help + } + + pub fn should_print_version(&self) -> bool { + self.print_version + } + + pub fn free(&self) -> &[String] { + &self.free + } + + fn get_pair(&self, key: &str) -> Option<&Pair> { + self.pairs.iter().find(|p| p.key.matches(key)) + } + + pub fn get(&self, key: &str) -> Option<&str> { + self.pairs + .iter() + .find(|p| p.key.matches(key)) + .map(|p| p.values.first().map(|s| s.as_str())) + .flatten() + } + + pub fn parse<T: FromStr, F: Into<Formula<T>>>(&self, key: &str, formula: F) -> Option<T> { + let formula = formula.into(); + let Some(got) = self.get(key) else { + return None; + }; + + match got.parse::<T>() { + Ok(o) => { + if let Some((fail, mut check)) = formula.check_fn { + if !check(&o) { + let pair = self.get_pair(key).unwrap(); + let str = fail.replace("[opt]", pair.key.preferred_key()); + + eprintln!("{str}"); + std::process::exit(-1); + } + + return Some(o); + }; + + Some(o) + } + Err(_e) => { + eprintln!("{}", formula.failure); + std::process::exit(-1); + } + } + } + + pub fn parse_req<T: FromStr, F: Into<Formula<T>>>(&self, key: &str, formula: F) -> T { + let formula = formula.into(); + let missing = formula.missing.clone(); + + match self.parse(key, formula) { + None => match missing { + None => { + let pair = self.get_pair(key).unwrap(); + eprintln!("An argument for '{}' is required", pair.key.preferred_key()); + std::process::exit(-1); + } + Some(misstr) => { + let pair = self.get_pair(key).unwrap(); + let str = misstr.replace("[opt]", pair.key.preferred_key()); + + eprintln!("{str}"); + std::process::exit(-1); + } + }, + Some(o) => o, + } + } +} + +pub struct Argument { + keys: Vec<String>, + arg_name: Option<&'static str>, + help: &'static str, +} + +impl Argument { + pub fn new<'s, K: Into<SingleOrMultiple<'s>>>(key: K) -> Self { + let keys = match key.into() { + SingleOrMultiple::Single(s) => vec![s], + SingleOrMultiple::Multiple(m) => m.to_vec(), + }; + + Self { + keys: keys.into_iter().map(<_>::to_owned).collect(), + arg_name: None, + help: "", + } + } + + pub fn arg(mut self, name: &'static str) -> Self { + self.arg_name = Some(name); + self + } + + pub fn help(mut self, help: &'static str) -> Self { + self.help = help; + self + } + + fn matches(&self, key: &str) -> bool { + self.keys.iter().find(|k| k.as_str() == key).is_some() + } + + fn preferred_key(&self) -> &str { + self.keys.first().unwrap().as_str() + } +} + +impl Into<Pair> for Argument { + fn into(self) -> Pair { + Pair { + key: self, + values: vec![], + } + } +} + +pub enum SingleOrMultiple<'s> { + Single(&'s str), + Multiple(&'s [&'s str]), +} + +impl<'s> From<&'s str> for SingleOrMultiple<'s> { + fn from(value: &'s str) -> Self { + Self::Single(value) + } +} + +impl<'s> From<&'s [&'s str]> for SingleOrMultiple<'s> { + fn from(value: &'s [&'s str]) -> Self { + Self::Multiple(value) + } +} + +impl<'s, const N: usize> From<&'s [&'s str; N]> for SingleOrMultiple<'s> { + fn from(value: &'s [&'s str; N]) -> Self { + Self::Multiple(value.as_slice()) + } +} |