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()) -    } -} | 
