about summary refs log tree commit diff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs203
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())
+	}
+}