diff options
author | gennyble <gen@nyble.dev> | 2025-04-29 14:45:01 -0500 |
---|---|---|
committer | gennyble <gen@nyble.dev> | 2025-04-29 14:45:01 -0500 |
commit | d7bdd477e266864e45ee33b29adba17bf3f70b20 (patch) | |
tree | 142745a311088c6ad274006bfb150f97758deea3 /src/formula.rs | |
download | scurvy-d7bdd477e266864e45ee33b29adba17bf3f70b20.tar.gz scurvy-d7bdd477e266864e45ee33b29adba17bf3f70b20.zip |
initial commit
Diffstat (limited to 'src/formula.rs')
-rw-r--r-- | src/formula.rs | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/src/formula.rs b/src/formula.rs new file mode 100644 index 0000000..3e84015 --- /dev/null +++ b/src/formula.rs @@ -0,0 +1,192 @@ +use core::fmt; +use std::{ + marker::PhantomData, + ops::{Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}, + path::PathBuf, + str::FromStr, +}; + +pub struct Formula<T: FromStr> { + pub(crate) check_fn: Option<(String, Box<dyn FnMut(&T) -> bool>)>, + pub(crate) failure: String, + pub(crate) missing: Option<String>, + phantom: PhantomData<T>, +} + +impl<T: FromStr> Formula<T> { + pub fn new<S: Into<String>>(failure: S) -> Self { + Self { + check_fn: None, + failure: failure.into(), + missing: None, + phantom: PhantomData::default(), + } + } + + pub fn check<S: Into<String>, 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<S: Into<String>>(mut self, missing: S) -> Self { + self.missing = Some(missing.into()); + self + } +} + +impl<T: FromStr> From<String> for Formula<T> { + fn from(value: String) -> Self { + Formula::new(value) + } +} + +impl<T: FromStr> From<&str> for Formula<T> { + fn from(value: &str) -> Self { + Formula::new(value) + } +} + +impl From<UsizeFormula> for Formula<usize> { + fn from(value: UsizeFormula) -> Self { + let UsizeFormula { 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 + } + } +} + +impl From<F32Formula> for Formula<f32> { + 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<PathFormula> for Formula<PathBuf> { + fn from(_value: PathFormula) -> Self { + Formula::new("Failed to parse [opt] as a path") + } +} + +pub struct F32Formula { + bounds: Option<Ranges<f32>>, +} + +impl F32Formula { + pub fn new() -> Self { + Self { bounds: None } + } + + pub fn bounds<R: Into<Ranges<f32>>>(mut self, bounds: R) -> Self { + self.bounds = Some(bounds.into()); + self + } +} + +pub struct UsizeFormula { + bounds: Option<Ranges<usize>>, +} + +impl UsizeFormula { + pub fn new() -> Self { + Self { bounds: None } + } + + pub fn bounds<R: Into<Ranges<usize>>>(mut self, bounds: R) -> Self { + self.bounds = Some(bounds.into()); + self + } +} + +pub enum Ranges<T> { + Exlusive(Range<T>), + Inclusive(RangeInclusive<T>), + From(RangeFrom<T>), + To(RangeTo<T>), + ToInclusive(RangeToInclusive<T>), +} + +impl<T> From<Range<T>> for Ranges<T> { + fn from(value: Range<T>) -> Self { + Ranges::Exlusive(value) + } +} + +impl<T> From<RangeFrom<T>> for Ranges<T> { + fn from(value: RangeFrom<T>) -> Self { + Ranges::From(value) + } +} + +impl<T> From<RangeTo<T>> for Ranges<T> { + fn from(value: RangeTo<T>) -> Self { + Ranges::To(value) + } +} + +impl<T> From<RangeInclusive<T>> for Ranges<T> { + fn from(value: RangeInclusive<T>) -> Self { + Ranges::Inclusive(value) + } +} + +impl<T> From<RangeToInclusive<T>> for Ranges<T> { + fn from(value: RangeToInclusive<T>) -> Self { + Ranges::ToInclusive(value) + } +} + +impl<T: fmt::Display> fmt::Display for Ranges<T> { + 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<T: PartialEq> Ranges<T> { + pub fn contains<U>(&self, item: &U) -> bool + where + T: PartialOrd<U>, + U: PartialOrd<T> + ?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 {} + } +} |