summaryrefslogtreecommitdiff
path: root/src/query.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/query.rs')
-rw-r--r--src/query.rs180
1 files changed, 180 insertions, 0 deletions
diff --git a/src/query.rs b/src/query.rs
new file mode 100644
index 0000000..4109488
--- /dev/null
+++ b/src/query.rs
@@ -0,0 +1,180 @@
+use crate::*;
+
+use log::fatal;
+
+use std::path::PathBuf;
+
+
+pub struct NamedSwitchQuery<'a> {
+ /// The borrowed switchboard instance.
+ pub switchboard: &'a mut Switchboard,
+ /// Long switch name.
+ pub name: String,
+ /// Short switch name.
+ pub short: Option<char>,
+ /// The default value if the switch hasn't been provided.
+ pub default: Option<String>,
+ /// The default value if the switch has been provided with no value.
+ pub quick: Option<String>,
+}
+
+impl NamedSwitchQuery<'_> {
+ pub fn short(mut self, short: char) -> Self {
+ if !short.is_ascii_alphanumeric() {
+ panic!("Invalid short name for argument: {short:?}");
+ }
+ self.short = Some(short);
+ self
+ }
+
+ pub fn default(mut self, value: &str) -> Self {
+ self.default = Some(value.to_string());
+ self
+ }
+
+ pub fn quick(mut self, value: &str) -> Self {
+ self.quick = Some(value.to_string());
+ self
+ }
+
+ fn debug_name(&self) -> String {
+ let mut debug_name = format!("--{}", self.name);
+ if let Some(short) = self.short {
+ debug_name.push_str(&format!(" (-{})", short));
+ }
+ debug_name
+ }
+}
+
+
+pub struct PositionalSwitchQuery<'a> {
+ /// The borrowed switchboard instance.
+ pub switchboard: &'a mut Switchboard,
+ /// The display name of this argument.
+ pub name: String,
+ /// The default value if the switch hasn't been provided.
+ pub default: Option<String>,
+}
+
+impl PositionalSwitchQuery<'_> {
+ pub fn default(mut self, value: &str) -> Self {
+ self.default = Some(value.to_string());
+ self
+ }
+}
+
+
+macro_rules! as_number {
+ ($type:ty, $name:expr, $error:ty) => {
+ paste::paste! {
+ fn [< as_ $type >](&mut self) -> $type {
+ self.[< as_ $type _opt >]().unwrap_or_else(|| self.missing("number"))
+ }
+ fn [< as_ $type _opt>](&mut self) -> Option<$type> {
+ self.get_value().map(|v| v.trim().parse().unwrap_or_else(
+ |e: $error| self.error(&v, $name, e.to_string())))
+ }
+ }
+ };
+}
+
+
+pub trait SwitchQuery {
+ fn get_name(&self) -> String;
+
+ fn get_value(&mut self) -> Option<String>;
+
+ fn error(&self, value: &str, ty: &str, err: String) -> ! {
+ let name = self.get_name();
+ fatal!("The {name} with value {value:?} could not be parsed as {ty}: {err}");
+ }
+
+ fn missing(&self, ty: &str) -> ! {
+ let name = self.get_name();
+ fatal!("The required {name} that takes a {ty} value was not provided");
+ }
+
+ fn as_bool(&mut self) -> bool {
+ if let Some(value) = self.get_value() {
+ match value.to_lowercase().as_str() {
+ "y"|"yes"|"t"|"true" => true,
+ "n"|"no" |"f"|"false" => false,
+ _ => true,
+ }
+ } else {
+ false
+ }
+ }
+
+ fn as_path(&mut self) -> PathBuf {
+ self.as_path_opt().unwrap_or_else(|| self.missing("path"))
+ }
+ fn as_path_opt(&mut self) -> Option<PathBuf> {
+ if let Some(value) = self.get_value() {
+ if !value.is_empty() {
+ return Some(PathBuf::from(value));
+ }
+ }
+ return None;
+ }
+
+ fn as_string(&mut self) -> String {
+ self.as_string_opt().unwrap_or_else(|| self.missing("string"))
+ }
+ fn as_string_opt(&mut self) -> Option<String> {
+ self.get_value()
+ }
+
+ as_number!{ f32, "f32" , std::num::ParseFloatError }
+ as_number!{ f64, "f64" , std::num::ParseFloatError }
+ as_number!{ u8, "u8" , std::num::ParseIntError }
+ as_number!{ u16, "u16" , std::num::ParseIntError }
+ as_number!{ u32, "u32" , std::num::ParseIntError }
+ as_number!{ u64, "u64" , std::num::ParseIntError }
+ as_number!{ usize, "usize", std::num::ParseIntError }
+ as_number!{ i8, "i8" , std::num::ParseIntError }
+ as_number!{ i16, "i16" , std::num::ParseIntError }
+ as_number!{ i32, "i32" , std::num::ParseIntError }
+ as_number!{ i64, "i64" , std::num::ParseIntError }
+ as_number!{ isize, "isize", std::num::ParseIntError }
+}
+
+
+impl SwitchQuery for NamedSwitchQuery<'_> {
+ fn get_name(&self) -> String {
+ format!("argument {}", self.debug_name())
+ }
+
+ fn get_value(&mut self) -> Option<String> {
+ // Find all matches first.
+ let mut matches = Vec::new();
+ for (i, switch) in self.switchboard.switches.iter().enumerate() {
+ match &switch.0 {
+ SwitchName::Short(other) => if let Some(short) = self.short {
+ if short == *other { matches.push(i) } }
+ SwitchName::Long(other) => if self.name == *other {
+ matches.push(i); },
+ };
+ }
+ match matches.len() {
+ 0 => self.default.clone(),
+ 1 => Some(
+ self.switchboard.switches.remove(matches[0]).1
+ .or_else(|| self.quick.clone())
+ .unwrap_or_else(String::new)
+ ),
+ _ => fatal!("The argument {} was passed more than once", self.debug_name()),
+ }
+ }
+}
+
+
+impl SwitchQuery for PositionalSwitchQuery<'_> {
+ fn get_name(&self) -> String {
+ format!("positional argument {:?}", self.name)
+ }
+
+ fn get_value(&mut self) -> Option<String> {
+ self.switchboard.positional.pop().or_else(|| self.default.clone())
+ }
+}