#[derive(Clone, Copy)]
pub struct Restrict<T>(T);
impl<T> Restrict<T> {
#[inline]
pub fn new(restricted: T) -> Self {
Self(restricted)
}
#[inline]
pub fn verify<'a, F: Fn(&'a T) -> bool>(&'a self, f: F) -> Verified<'a, T> {
if f(&self.0) {
Verified(VerifiedInner::Valid(&self.0))
} else {
Verified(VerifiedInner::Invalid(&self.0))
}
}
#[inline]
pub fn verify_unwrap<F: Fn(&T) -> bool>(self, f: F) -> Result<T, T> {
if f(&self.0) {
Ok(self.0)
} else {
Err(self.0)
}
}
#[inline]
pub fn unverified(self) -> T {
self.0
}
#[inline]
pub fn map<R, F: Fn(T) -> R>(self, f: F) -> Restrict<R> {
Restrict(f(self.0))
}
}
pub struct Verified<'a, T>(VerifiedInner<'a, T>);
impl<'a, T> Verified<'a, T> {
#[inline]
pub fn then<R, F: Fn(&T) -> R>(&self, f: F) -> Result<R, &T> {
match self.0 {
VerifiedInner::Valid(t) => Ok(f(t)),
VerifiedInner::Invalid(t) => Err(t),
}
}
#[inline]
pub fn is_valid(&self) -> bool {
match self.0 {
VerifiedInner::Valid(_) => true,
VerifiedInner::Invalid(_) => false,
}
}
}
enum VerifiedInner<'a, T> {
Valid(&'a T),
Invalid(&'a T),
}
pub trait RestrictedMath {
type Arg: 'static + Sized + Copy;
type Value: 'static + Sized + Copy;
fn checked_add(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg>;
fn checked_sub(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg>;
fn checked_mul(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg>;
}
impl RestrictedMath for Restrict<usize> {
type Arg = usize;
type Value = usize;
fn checked_add(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
self.0.checked_add(arg).map(Restrict).ok_or(arg)
}
fn checked_sub(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
self.0.checked_sub(arg).map(Restrict).ok_or(arg)
}
fn checked_mul(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
self.0.checked_mul(arg).map(Restrict).ok_or(arg)
}
}
impl RestrictedMath for Restrict<u8> {
type Arg = u8;
type Value = u8;
fn checked_add(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
self.0.checked_add(arg).map(Restrict).ok_or(arg)
}
fn checked_sub(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
self.0.checked_sub(arg).map(Restrict).ok_or(arg)
}
fn checked_mul(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
self.0.checked_mul(arg).map(Restrict).ok_or(arg)
}
}
impl RestrictedMath for Restrict<u16> {
type Arg = u16;
type Value = u16;
fn checked_add(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
self.0.checked_add(arg).map(Restrict).ok_or(arg)
}
fn checked_sub(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
self.0.checked_sub(arg).map(Restrict).ok_or(arg)
}
fn checked_mul(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
self.0.checked_mul(arg).map(Restrict).ok_or(arg)
}
}
impl<R, A> RestrictedMath for Result<R, A>
where
R: RestrictedMath,
A: 'static + Sized + Copy,
{
type Arg = <R as RestrictedMath>::Arg;
type Value = <R as RestrictedMath>::Value;
fn checked_add(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
match *self {
Ok(ref r) => r.checked_add(arg),
Err(_) => Err(arg),
}
}
fn checked_sub(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
match *self {
Ok(ref r) => r.checked_sub(arg),
Err(_) => Err(arg),
}
}
fn checked_mul(&self, arg: Self::Arg) -> Result<Restrict<Self::Value>, Self::Arg> {
match *self {
Ok(ref r) => r.checked_mul(arg),
Err(_) => Err(arg),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_checked_add() {
assert_eq!(
Restrict(1_usize).checked_add(2_usize).unwrap().unverified(),
3_usize
);
assert_eq!(
Restrict(1_u16).checked_add(2_u16).unwrap().unverified(),
3_u16
);
assert_eq!(Restrict(1_u8).checked_add(2_u8).unwrap().unverified(), 3_u8);
}
#[test]
fn test_checked_sub() {
assert_eq!(
Restrict(2_usize).checked_sub(1_usize).unwrap().unverified(),
1_usize
);
assert_eq!(
Restrict(2_u16).checked_sub(1_u16).unwrap().unverified(),
1_u16
);
assert_eq!(Restrict(2_u8).checked_sub(1_u8).unwrap().unverified(), 1_u8);
}
#[test]
fn test_checked_mul() {
assert_eq!(
Restrict(1_usize).checked_mul(2_usize).unwrap().unverified(),
2_usize
);
assert_eq!(
Restrict(1_u16).checked_mul(2_u16).unwrap().unverified(),
2_u16
);
assert_eq!(Restrict(1_u8).checked_mul(2_u8).unwrap().unverified(), 2_u8);
}
}