diff options
Diffstat (limited to 'src/query.rs')
-rw-r--r-- | src/query.rs | 236 |
1 files changed, 119 insertions, 117 deletions
diff --git a/src/query.rs b/src/query.rs index 4109488..5a9d4ae 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1,9 +1,5 @@ use crate::*; -use log::fatal; - -use std::path::PathBuf; - pub struct NamedSwitchQuery<'a> { /// The borrowed switchboard instance. @@ -12,31 +8,99 @@ pub struct NamedSwitchQuery<'a> { 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>, + /// Documentation string. + pub doc: Option<String>, + /// True if this query has already been committed. + pub committed: bool, } impl NamedSwitchQuery<'_> { + pub fn doc(mut self, doc: &str) -> Self { + self.doc = Some(doc.to_string()); + self + } + pub fn short(mut self, short: char) -> Self { if !short.is_ascii_alphanumeric() { - panic!("Invalid short name for argument: {short:?}"); + fatal!("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 } + pub fn optional(&mut self) { + let value = self.get_value(); + self.insert(value); + } + + pub fn default(&mut self, default: &str) { + match self.get_value() { + Some(value) => self.insert(Some(value)), + None => self.insert(Some(Some(default.to_string()))), + }; + } + + pub fn required(&mut self) { + match self.get_value() { + Some(value) => self.insert(Some(value)), + None => { + let error = QueryError::MissingNamed(self.debug_name()); + self.switchboard.errors.push(error); + } + } + } + + fn insert(&mut self, value: Option<Option<String>>) { + let queried = QueriedValue { + doc: self.doc.clone(), + name: self.name.clone(), + variant: QueryVariant::Named(self.debug_name()), + value, + }; + if let Some(_) = self.switchboard.values.insert(self.name.clone(), queried) { + error!("Duplicate query for name {:?}", self.name.clone()); + } + self.committed = true; + } + + fn get_value(&mut self) -> Option<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); + }, + }; + } + // Return a value. + match matches.len() { + 0 => None, + 1 => { + let found = self.switchboard.switches.remove(matches[0]).1; + match found { + Some(string) => Some(Some(string)), + None => Some(self.quick.clone()), + } + } + _ => { + let error = QueryError::Repeated(self.debug_name()); + self.switchboard.errors.push(error); + None + } + } + } + fn debug_name(&self) -> String { let mut debug_name = format!("--{}", self.name); if let Some(short) = self.short { @@ -46,135 +110,73 @@ impl NamedSwitchQuery<'_> { } } +impl Drop for NamedSwitchQuery<'_> { + fn drop(&mut self) { + if !self.committed { + self.optional(); + } + } +} + + 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>, + /// Documentation string. + pub doc: Option<String>, + /// True if this query has already been committed. + pub committed: bool, } impl PositionalSwitchQuery<'_> { - pub fn default(mut self, value: &str) -> Self { - self.default = Some(value.to_string()); + pub fn doc(mut self, doc: &str) -> Self { + self.doc = Some(doc.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}"); + pub fn optional(&mut self) { + let value = self.switchboard.pop(); + self.insert(value); } - fn missing(&self, ty: &str) -> ! { - let name = self.get_name(); - fatal!("The required {name} that takes a {ty} value was not provided"); + pub fn default(&mut self, default: &str) { + match self.switchboard.pop() { + Some(value) => self.insert(Some(value)), + None => self.insert(Some(default.to_string())), + }; } - 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, + pub fn required(&mut self) { + match self.switchboard.pop() { + Some(value) => self.insert(Some(value)), + None => { + let error = QueryError::MissingPositional(self.name.clone()); + self.switchboard.errors.push(error); } - } 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)); - } + fn insert(&mut self, value: Option<String>) { + let queried = QueriedValue { + doc: self.doc.clone(), + name: self.name.clone(), + variant: QueryVariant::Positional(self.switchboard.i), + value: Some(value), + }; + if let Some(_) = self.switchboard.values.insert(self.name.clone(), queried) { + error!("Duplicate query for name {:?}", self.name.clone()); } - return None; + self.committed = true; } - - 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 Drop for PositionalSwitchQuery<'_> { + fn drop(&mut self) { + if !self.committed { + self.optional(); } } } - - -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()) - } -} |