use core::fmt; use std::{ marker::PhantomData, ops::{Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}, path::PathBuf, str::FromStr, }; pub struct Formula { pub(crate) check_fn: Option<(String, Box bool>)>, pub(crate) failure: String, pub(crate) missing: Option, phantom: PhantomData, } impl Formula { pub fn new>(failure: S) -> Self { Self { check_fn: None, failure: failure.into(), missing: None, phantom: PhantomData::default(), } } pub fn check, F>(mut self, fail: S, check: F) -> Self where F: FnMut(&T) -> bool + 'static, { self.check_fn = Some((fail.into(), Box::new(check))); self } pub fn required>(mut self, missing: S) -> Self { self.missing = Some(missing.into()); self } } impl From for Formula { fn from(value: String) -> Self { Formula::new(value) } } impl From<&str> for Formula { fn from(value: &str) -> Self { Formula::new(value) } } impl From for Formula { fn from(value: F32Formula) -> Self { let F32Formula { bounds } = value; let form = Formula::new("Failed to parse [opt] as a number"); if let Some(bounds) = bounds { let fail = format!("[opt] must be {bounds}"); form.check(fail, move |u| bounds.contains(&u)) } else { form } } } impl From for Formula { fn from(_value: PathFormula) -> Self { Formula::new("Failed to parse [opt] as a path") } } pub struct F32Formula { bounds: Option>, } impl F32Formula { pub fn new() -> Self { Self { bounds: None } } pub fn bounds>>(mut self, bounds: R) -> Self { self.bounds = Some(bounds.into()); self } } pub enum Ranges { Exlusive(Range), Inclusive(RangeInclusive), From(RangeFrom), To(RangeTo), ToInclusive(RangeToInclusive), } impl From> for Ranges { fn from(value: Range) -> Self { Ranges::Exlusive(value) } } impl From> for Ranges { fn from(value: RangeFrom) -> Self { Ranges::From(value) } } impl From> for Ranges { fn from(value: RangeTo) -> Self { Ranges::To(value) } } impl From> for Ranges { fn from(value: RangeInclusive) -> Self { Ranges::Inclusive(value) } } impl From> for Ranges { fn from(value: RangeToInclusive) -> Self { Ranges::ToInclusive(value) } } impl fmt::Display for Ranges { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Ranges::Exlusive(range) => { write!(f, "from {} and less than {}", range.start, range.end) } Ranges::Inclusive(range) => write!(f, "from {} to {}", range.start(), range.end()), Ranges::From(range) => write!(f, "greater than {}", range.start), Ranges::To(range) => write!(f, "less than {}", range.end), Ranges::ToInclusive(range) => write!(f, "from 0 to {}", range.end), } } } impl Ranges { pub fn contains(&self, item: &U) -> bool where T: PartialOrd, U: PartialOrd + ?Sized, { match self { Ranges::Exlusive(range) => range.contains(item), Ranges::Inclusive(range) => range.contains(item), Ranges::From(range) => range.contains(item), Ranges::To(range) => range.contains(item), Ranges::ToInclusive(range) => range.contains(item), } } } pub struct PathFormula; impl PathFormula { pub fn new() -> Self { Self {} } } macro_rules! int_formula_impl { ($meow:ident $woof:ty) => { pub struct $meow { bounds: Option>, } impl $meow { pub fn new() -> Self { Self { bounds: None } } pub fn bounds>>(mut self, bounds: R) -> Self { self.bounds = Some(bounds.into()); self } } impl From<$meow> for Formula<$woof> { fn from(value: $meow) -> Self { let $meow { bounds } = value; let form = Formula::new("Failed to parse [opt] as an integer"); if let Some(bounds) = bounds { let fail = format!("[opt] must be {bounds}"); form.check(fail, move |u| bounds.contains(&u)) } else { form } } } }; } int_formula_impl!(U8Formula u8); int_formula_impl!(U16Formula u16); int_formula_impl!(U32Formula u32); int_formula_impl!(U64Formula u64); int_formula_impl!(UsizeFormula usize); int_formula_impl!(I8Formula i8); int_formula_impl!(I16Formula i16); int_formula_impl!(I32Formula i32); int_formula_impl!(I64Formula i64); int_formula_impl!(IsizeFormula isize);