#![allow(box_pointers, clippy::tests_outside_test_module, clippy::std_instead_of_core)]
use std::{
convert::{TryFrom, TryInto},
error::Error,
ops::{Bound, Range, RangeBounds, RangeInclusive},
};
use ranges::{Domain, GenericIterator, GenericRange, Iterable};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct TwoDigitInt(u8);
impl TwoDigitInt {
const MIN: Self = Self(10);
const MAX: Self = Self(99);
}
impl TryFrom<u8> for TwoDigitInt {
type Error = Box<dyn Error>;
fn try_from(val: u8) -> Result<Self, Self::Error> {
if &val < Self::MIN.as_ref() || Self::MAX.as_ref() < &val {
Err("input out of bounds".into())
} else {
Ok(Self(val))
}
}
}
impl AsRef<u8> for TwoDigitInt {
fn as_ref(&self) -> &u8 {
&self.0
}
}
impl Domain for TwoDigitInt {
const DISCRETE: bool = true;
fn minimum() -> Bound<Self> {
Bound::Included(Self::MIN)
}
fn maximum() -> Bound<Self> {
Bound::Included(Self::MAX)
}
fn predecessor(&self) -> Option<Self> {
(self.as_ref() - 1).try_into().ok()
}
fn successor(&self) -> Option<Self> {
(self.as_ref() + 1).try_into().ok()
}
}
impl Iterable for TwoDigitInt {
type Output = Self;
fn next(&self) -> Option<Self::Output> {
self.successor()
}
}
fn try_map_bound<F, T, U, E>(bound: Bound<T>, f: F) -> Result<Bound<U>, E>
where
F: FnOnce(T) -> Result<U, E>,
{
match bound {
Bound::Included(t) => Ok(Bound::Included(f(t)?)),
Bound::Excluded(t) => Ok(Bound::Excluded(f(t)?)),
Bound::Unbounded => Ok(Bound::Unbounded),
}
}
trait RangeBoundsExt<T>: RangeBounds<T>
where
T: Clone,
{
fn try_map<F, U, E>(&self, f: F) -> Result<(Bound<U>, Bound<U>), E>
where
F: FnOnce(T) -> Result<U, E> + Clone,
{
Ok((
try_map_bound(self.start_bound().cloned(), f.clone())?,
try_map_bound(self.end_bound().cloned(), f)?,
))
}
}
impl<T: Clone, B: RangeBounds<T>> RangeBoundsExt<T> for B {}
struct TwoDigitIntRange(GenericRange<TwoDigitInt>);
impl TwoDigitIntRange {
fn from<R: TryInto<Self>>(range: R) -> Result<Self, R::Error> {
range.try_into()
}
}
macro_rules! impl_try_from {
( $range_ty:ident ) => {
impl<T> TryFrom<$range_ty<T>> for TwoDigitIntRange
where
T: TryInto<TwoDigitInt, Error = Box<dyn Error>> + Clone,
{
type Error = <T as TryInto<TwoDigitInt>>::Error;
fn try_from(range: $range_ty<T>) -> Result<Self, Self::Error> {
Ok(Self(range.try_map(|t| t.try_into())?.into()))
}
}
};
}
impl_try_from!(Range);
impl_try_from!(RangeInclusive);
impl IntoIterator for TwoDigitIntRange {
type Item = TwoDigitInt;
type IntoIter = TwoDigitIntRangeIter;
fn into_iter(self) -> Self::IntoIter {
TwoDigitIntRangeIter(self.0.into_iter())
}
}
struct TwoDigitIntRangeIter(GenericIterator<TwoDigitInt>);
impl Iterator for TwoDigitIntRangeIter {
type Item = TwoDigitInt;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[test]
fn empty() {
let mut iter = TwoDigitIntRange::from(50..50)
.unwrap()
.into_iter()
.map(|i| i.as_ref().to_owned());
assert_eq!(iter.next(), None);
}
#[test]
fn open() {
let mut iter = TwoDigitIntRange::from(10..12)
.unwrap()
.into_iter()
.map(|i| i.as_ref().to_owned());
assert_eq!(iter.next(), Some(10));
assert_eq!(iter.next(), Some(11));
assert_eq!(iter.next(), None);
}
#[test]
fn closed() {
let mut iter = TwoDigitIntRange::from(97..=99)
.unwrap()
.into_iter()
.map(|i| i.as_ref().to_owned());
assert_eq!(iter.next(), Some(97));
assert_eq!(iter.next(), Some(98));
assert_eq!(iter.next(), Some(99));
assert_eq!(iter.next(), None);
}
#[test]
#[should_panic(expected = "Result::unwrap()` on an `Err` value: \"input out of bounds\"")]
fn invalid() {
TwoDigitIntRange::from(5..15).unwrap();
}