mod non_negative;
mod non_positive;
mod non_zero;
mod strictly_negative;
mod strictly_positive;
mod unit_interval;
use std::{iter::Sum, marker::PhantomData, ops::Add};
use num_traits::Zero;
use thiserror::Error;
pub use non_negative::NonNegative;
pub use non_positive::NonPositive;
pub use non_zero::NonZero;
pub use strictly_negative::StrictlyNegative;
pub use strictly_positive::StrictlyPositive;
pub use unit_interval::{
UnitBounds, UnitInterval, UnitIntervalLowerOpen, UnitIntervalOpen, UnitIntervalUpperOpen,
};
pub trait Constraint<T> {
fn check(value: &T) -> Result<(), ConstraintError>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[non_exhaustive]
pub enum ConstraintError {
#[error("value must not be negative")]
Negative,
#[error("value must not be positive")]
Positive,
#[error("value must not be zero")]
Zero,
#[error("value is not a number")]
NotANumber,
#[error("value is below the minimum allowed")]
BelowMinimum,
#[error("value is above the maximum allowed")]
AboveMaximum,
}
pub type ConstraintResult<T, E = ConstraintError> = Result<T, E>;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Constrained<T, C: Constraint<T>> {
value: T,
_marker: PhantomData<C>,
}
impl<T, C: Constraint<T>> Constrained<T, C> {
pub fn new(value: T) -> Result<Self, ConstraintError> {
C::check(&value)?;
Ok(Self {
value,
_marker: PhantomData,
})
}
pub fn into_inner(self) -> T {
self.value
}
}
impl<T, C: Constraint<T>> AsRef<T> for Constrained<T, C> {
fn as_ref(&self) -> &T {
&self.value
}
}
impl<T, C> Sum for Constrained<T, C>
where
C: Constraint<T>,
Constrained<T, C>: Add<Output = Self> + Zero,
{
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::zero(), |a, b| a + b)
}
}