about summary refs log tree commit diff
path: root/src/formula.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/formula.rs')
-rw-r--r--src/formula.rs192
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 {}
+	}
+}