use std::fmt;
pub trait Bound: crate::private::Sealed {
type Value: PartialOrd;
type WithLimit: Bound<Value = Self::Value>;
fn value(&self) -> Option<&Self::Value>;
fn is_open(&self) -> bool;
fn is_closed(&self) -> bool;
fn with_limit_point(self) -> Self::WithLimit;
}
pub trait ProperBound: Bound {
fn proper_value(&self) -> &Self::Value;
}
pub trait BoundDisplay: Bound {
fn fmt_left(&self, f: &mut fmt::Formatter) -> fmt::Result;
fn fmt_right(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
pub trait Pinch<T>: Bound {
type Left: Bound<Value = Self::Value>;
type Right: Bound<Value = Self::Value>;
fn pinch_left(self, other: T) -> Self::Left;
fn pinch_right(self, other: T) -> Self::Right;
}
pub trait Unroll<T>: Bound {
type Left: Bound<Value = Self::Value>;
type Right: Bound<Value = Self::Value>;
fn unroll_left(self, other: T) -> Self::Left;
fn unroll_right(self, other: T) -> Self::Right;
}
mod no_bound;
pub use self::no_bound::NoBound;
mod open;
pub use self::open::Open;
mod closed;
pub use self::closed::Closed;
mod mixed;
pub use self::mixed::OpenOrClosed;
pub fn validate<L: Bound, R: Bound>(left: L, right: R) -> ValidationResult<L, R>
where
Validator: ValidateBounds<L, R>,
{
<Validator as ValidateBounds<L, R>>::validate(left, right)
}
pub struct Validator;
#[derive(Debug, Clone, Copy)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
pub enum ValidationError<L, R> {
DecreasingBounds(L, R)
}
pub type ValidationResult<L, R> = Result<(L, R), ValidationError<L, R>>;
pub trait ValidateBounds<L: Bound, R: Bound> {
fn validate(left: L, right: R) -> ValidationResult<L, R>;
}
macro_rules! impl_val {
($v:ident; NoBound, $r:ty) => {
impl<V: PartialOrd> ValidateBounds<NoBound<$v>, $r> for Validator {
fn validate(l: NoBound<$v>, r: $r) -> ValidationResult<NoBound<$v>, $r> { Ok((l, r)) }
}
};
($v:ident; $l:ty, NoBound) => {
impl<V: PartialOrd> ValidateBounds<$l, NoBound<$v>> for Validator {
fn validate(l: $l, r: NoBound<$v>) -> ValidationResult<$l, NoBound<$v>> { Ok((l, r)) }
}
};
($v:ident; $l:ty, $r:ty) => {
impl<$v: PartialOrd> ValidateBounds<$l, $r> for Validator {
fn validate(l: $l, r: $r) -> ValidationResult<$l, $r> {
if l.proper_value() > r.proper_value() {
Err(ValidationError::DecreasingBounds(l, r))
} else {
Ok((l, r))
}
}
}
};
}
impl<V: PartialOrd> ValidateBounds<NoBound<V>, NoBound<V>> for Validator {
fn validate(l: NoBound<V>, r: NoBound<V>) -> ValidationResult<NoBound<V>, NoBound<V>> {
Ok((l, r))
}
}
impl_val!(V; NoBound, Open<V>);
impl_val!(V; NoBound, Closed<V>);
impl_val!(V; NoBound, OpenOrClosed<V>);
impl_val!(V; Open<V>, NoBound);
impl_val!(V; Closed<V>, NoBound);
impl_val!(V; Closed<V>, Closed<V>);
impl_val!(V; OpenOrClosed<V>, NoBound);
impl<V: PartialOrd> ValidateBounds<Closed<V>, Open<V>> for Validator {
fn validate(l: Closed<V>, r: Open<V>) -> ValidationResult<Closed<V>, Open<V>> {
if l.0 >= r.0 {
Err(ValidationError::DecreasingBounds(l, r))
} else {
Ok((l, r))
}
}
}
impl<V: PartialOrd> ValidateBounds<Open<V>, Closed<V>> for Validator {
fn validate(l: Open<V>, r: Closed<V>) -> ValidationResult<Open<V>, Closed<V>> {
if l.0 >= r.0 {
Err(ValidationError::DecreasingBounds(l, r))
} else {
Ok((l, r))
}
}
}
impl<V: PartialOrd> ValidateBounds<OpenOrClosed<V>, Closed<V>> for Validator {
fn validate(l: OpenOrClosed<V>, r: Closed<V>) -> ValidationResult<OpenOrClosed<V>, Closed<V>> {
let is_invalid = match &l {
OpenOrClosed::Open(x) => x >= &r.0,
OpenOrClosed::Closed(x) => x > &r.0,
};
if is_invalid {
Err(ValidationError::DecreasingBounds(l, r))
} else {
Ok((l, r))
}
}
}
impl<V: PartialOrd> ValidateBounds<Closed<V>, OpenOrClosed<V>> for Validator {
fn validate(l: Closed<V>, r: OpenOrClosed<V>) -> ValidationResult<Closed<V>, OpenOrClosed<V>> {
let is_invalid = match &r {
OpenOrClosed::Open(x) => &l.0 >= x,
OpenOrClosed::Closed(x) => &l.0 > x,
};
if is_invalid {
Err(ValidationError::DecreasingBounds(l, r))
} else {
Ok((l, r))
}
}
}
impl<V: PartialOrd> ValidateBounds<Open<V>, Open<V>> for Validator {
fn validate(l: Open<V>, r: Open<V>) -> ValidationResult<Open<V>, Open<V>> {
if l.0 >= r.0 {
Err(ValidationError::DecreasingBounds(l, r))
} else {
Ok((l, r))
}
}
}
impl<V: PartialOrd> ValidateBounds<OpenOrClosed<V>, Open<V>> for Validator {
fn validate(l: OpenOrClosed<V>, r: Open<V>) -> ValidationResult<OpenOrClosed<V>, Open<V>> {
let is_invalid = match &l {
OpenOrClosed::Open(x) | OpenOrClosed::Closed(x) => x >= &r.0,
};
if is_invalid {
Err(ValidationError::DecreasingBounds(l, r))
} else {
Ok((l, r))
}
}
}
impl<V: PartialOrd> ValidateBounds<Open<V>, OpenOrClosed<V>> for Validator {
fn validate(l: Open<V>, r: OpenOrClosed<V>) -> ValidationResult<Open<V>, OpenOrClosed<V>> {
let is_invalid = match &r {
OpenOrClosed::Open(x) | OpenOrClosed::Closed(x) => &l.0 >= x,
};
if is_invalid {
Err(ValidationError::DecreasingBounds(l, r))
} else {
Ok((l, r))
}
}
}
impl<V: PartialOrd> ValidateBounds<OpenOrClosed<V>, OpenOrClosed<V>> for Validator {
fn validate(l: OpenOrClosed<V>, r: OpenOrClosed<V>) -> ValidationResult<OpenOrClosed<V>, OpenOrClosed<V>> {
let is_invalid = match (&l, &r) {
(&OpenOrClosed::Open(ref x), &OpenOrClosed::Open(ref y))
| (&OpenOrClosed::Closed(ref x), &OpenOrClosed::Open(ref y))
| (&OpenOrClosed::Open(ref x), &OpenOrClosed::Closed(ref y)) => x >= y,
(&OpenOrClosed::Closed(ref x), &OpenOrClosed::Closed(ref y)) => x > y,
};
if is_invalid {
Err(ValidationError::DecreasingBounds(l, r))
} else {
Ok((l, r))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_unbounded() {
assert!(validate(NoBound::<f64>::new(), NoBound::<f64>::new()).is_ok());
assert!(validate(NoBound::new(), Open(0.0f64)).is_ok());
assert!(validate(NoBound::new(), Closed(0.0f64)).is_ok());
assert!(validate(NoBound::new(), OpenOrClosed::Open(0.0f64)).is_ok());
assert!(validate(NoBound::new(), OpenOrClosed::Closed(0.0f64)).is_ok());
assert!(validate(Open(0.0f64), NoBound::new()).is_ok());
assert!(validate(Closed(0.0f64), NoBound::new()).is_ok());
assert!(validate(OpenOrClosed::Open(0.0f64), NoBound::new()).is_ok());
assert!(validate(OpenOrClosed::Closed(0.0f64), NoBound::new()).is_ok());
}
#[test]
fn test_validate_allopen() {
assert!(validate(Open(0.0f64), Open(-1.0f64)).is_err());
assert!(validate(Open(0.0f64), Open(0.0f64)).is_err());
assert!(validate(Open(0.0f64), Open(1.0f64)).is_ok());
assert!(validate(Open(0.0f64), OpenOrClosed::Open(-1.0f64)).is_err());
assert!(validate(Open(0.0f64), OpenOrClosed::Open(0.0f64)).is_err());
assert!(validate(Open(0.0f64), OpenOrClosed::Open(1.0f64)).is_ok());
assert!(validate(OpenOrClosed::Open(0.0f64), Open(-1.0f64)).is_err());
assert!(validate(OpenOrClosed::Open(0.0f64), Open(0.0f64)).is_err());
assert!(validate(OpenOrClosed::Open(0.0f64), Open(1.0f64)).is_ok());
assert!(validate(OpenOrClosed::Open(0.0f64), OpenOrClosed::Open(-1.0f64)).is_err());
assert!(validate(OpenOrClosed::Open(0.0f64), OpenOrClosed::Open(0.0f64)).is_err());
assert!(validate(OpenOrClosed::Open(0.0f64), OpenOrClosed::Open(1.0f64)).is_ok());
}
#[test]
fn test_validate_closed() {
assert!(validate(Closed(0.0f64), Closed(-1.0f64)).is_err());
assert!(validate(Closed(0.0f64), Closed(0.0f64)).is_ok());
assert!(validate(Closed(0.0f64), Closed(1.0f64)).is_ok());
assert!(validate(Closed(0.0f64), OpenOrClosed::Closed(-1.0f64)).is_err());
assert!(validate(Closed(0.0f64), OpenOrClosed::Closed(0.0f64)).is_ok());
assert!(validate(Closed(0.0f64), OpenOrClosed::Closed(1.0f64)).is_ok());
assert!(validate(OpenOrClosed::Closed(0.0f64), Closed(-1.0f64)).is_err());
assert!(validate(OpenOrClosed::Closed(0.0f64), Closed(0.0f64)).is_ok());
assert!(validate(OpenOrClosed::Closed(0.0f64), Closed(1.0f64)).is_ok());
}
#[test]
fn test_validate_mixed() {
assert!(validate(Closed(0.0f64), Open(-1.0f64)).is_err());
assert!(validate(Closed(0.0f64), Open(0.0f64)).is_err());
assert!(validate(Closed(0.0f64), Open(1.0f64)).is_ok());
assert!(validate(Closed(0.0f64), OpenOrClosed::Open(-1.0f64)).is_err());
assert!(validate(Closed(0.0f64), OpenOrClosed::Open(0.0f64)).is_err());
assert!(validate(Closed(0.0f64), OpenOrClosed::Open(1.0f64)).is_ok());
assert!(validate(OpenOrClosed::Closed(0.0f64), Open(-1.0f64)).is_err());
assert!(validate(OpenOrClosed::Closed(0.0f64), Open(0.0f64)).is_err());
assert!(validate(OpenOrClosed::Closed(0.0f64), Open(1.0f64)).is_ok());
assert!(validate(OpenOrClosed::Closed(0.0f64), OpenOrClosed::Open(-1.0f64)).is_err());
assert!(validate(OpenOrClosed::Closed(0.0f64), OpenOrClosed::Open(0.0f64)).is_err());
assert!(validate(OpenOrClosed::Closed(0.0f64), OpenOrClosed::Open(1.0f64)).is_ok());
assert!(validate(Open(0.0f64), Closed(-1.0f64)).is_err());
assert!(validate(Open(0.0f64), Closed(0.0f64)).is_err());
assert!(validate(Open(0.0f64), Closed(1.0f64)).is_ok());
assert!(validate(Open(0.0f64), OpenOrClosed::Closed(-1.0f64)).is_err());
assert!(validate(Open(0.0f64), OpenOrClosed::Closed(0.0f64)).is_err());
assert!(validate(Open(0.0f64), OpenOrClosed::Closed(1.0f64)).is_ok());
assert!(validate(OpenOrClosed::Open(0.0f64), Closed(-1.0f64)).is_err());
assert!(validate(OpenOrClosed::Open(0.0f64), Closed(0.0f64)).is_err());
assert!(validate(OpenOrClosed::Open(0.0f64), Closed(1.0f64)).is_ok());
assert!(validate(OpenOrClosed::Open(0.0f64), OpenOrClosed::Closed(-1.0f64)).is_err());
assert!(validate(OpenOrClosed::Open(0.0f64), OpenOrClosed::Closed(0.0f64)).is_err());
assert!(validate(OpenOrClosed::Open(0.0f64), OpenOrClosed::Closed(1.0f64)).is_ok());
}
}