summaryrefslogtreecommitdiff
path: root/src/query.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/query.rs')
-rw-r--r--src/query.rs236
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())
- }
-}