summaryrefslogtreecommitdiff
path: root/src/value.rs
diff options
context:
space:
mode:
authorBen Bridle <ben@derelict.engineering>2025-03-03 20:51:01 +1300
committerBen Bridle <ben@derelict.engineering>2025-03-03 20:54:45 +1300
commit8a43a02b6950455aedbbdbee737bee1654aa91ef (patch)
tree64e31ff1cfbbdce22e104adcb1ad81f051019ca1 /src/value.rs
parentea70fa89659e5cf1a9d4ca6ea31fb67f7a2cc633 (diff)
downloadswitchboard-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.rs112
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");
+ }
+}