diff options
author | Ben Bridle <ben@derelict.engineering> | 2025-03-03 20:51:01 +1300 |
---|---|---|
committer | Ben Bridle <ben@derelict.engineering> | 2025-03-03 20:54:45 +1300 |
commit | 8a43a02b6950455aedbbdbee737bee1654aa91ef (patch) | |
tree | 64e31ff1cfbbdce22e104adcb1ad81f051019ca1 /src/value.rs | |
parent | ea70fa89659e5cf1a9d4ca6ea31fb67f7a2cc633 (diff) | |
download | switchboard-8a43a02b6950455aedbbdbee737bee1654aa91ef.zip |
Implement error reporting
The library has been redesigned so that all queries can be entered
before any values are parsed. This allows all errors unrelated to value
parsing to be displayed as a batch.
Diffstat (limited to 'src/value.rs')
-rw-r--r-- | src/value.rs | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/src/value.rs b/src/value.rs new file mode 100644 index 0000000..99468fa --- /dev/null +++ b/src/value.rs @@ -0,0 +1,112 @@ +use crate::*; + +use std::path::PathBuf; + + +macro_rules! as_number { + ($type:ty, $name:expr, $error:ty) => { + paste::paste! { + pub fn [< as_ $type >](&mut self) -> $type { + self.[< as_ $type _opt >]().unwrap_or_else(|| self.missing("number")) + } + pub fn [< as_ $type _opt>](&mut self) -> Option<$type> { + self.value().as_ref().map(|v| v.trim().parse().unwrap_or_else( + |e: $error| self.error(&v, $name, e.to_string()))) + } + } + }; +} + + +pub struct QueriedValue { + pub doc: Option<String>, + pub name: String, + pub variant: QueryVariant, + /// Some if switch passed, then some if value passed. + pub value: Option<Option<String>>, +} + +pub enum QueryVariant { + Positional(usize), + Named(String), +} + +impl QueriedValue { + pub fn value(&self) -> Option<&String> { + match &self.value { + Some(value) => value.as_ref(), + None => None, + } + } + + pub fn as_bool(self) -> bool { + if let Some(value) = self.value { + if let Some(value) = value { + match value.to_lowercase().as_str() { + "y"|"yes"|"t"|"true" => true, + "n"|"no" |"f"|"false" => false, + _ => true, + } + } else { + true + } + } else { + false + } + } + + pub fn as_path(&mut self) -> PathBuf { + self.as_path_opt().unwrap_or_else(|| self.missing("path")) + } + pub fn as_path_opt(&mut self) -> Option<PathBuf> { + if let Some(value) = self.value() { + if !value.is_empty() { + return Some(PathBuf::from(value)); + } + } + return None; + } + + pub fn as_string(&mut self) -> String { + self.as_string_opt().unwrap_or_else(|| self.missing("string")) + } + pub fn as_string_opt(&mut self) -> Option<String> { + self.value().cloned() + } + + 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 } + + + fn locator(&self) -> String { + match &self.variant { + QueryVariant::Positional(_) => { + let name = &self.name; + format!("<{name}> argument") + } + QueryVariant::Named(name) => { + format!("{name} switch") + } + } + } + + fn error(&self, value: &str, ty: &str, err: String) -> ! { + let locator = self.locator(); + fatal!("The value {value:?} passed to the {locator} could not be parsed as a {ty}: {err}"); + } + + fn missing(&self, _ty: &str) -> ! { + let locator = self.locator(); + fatal!("The {locator} is required"); + } +} |