use crate::{
iterators::IntervalIterator,
numerated::{Bound, Numerated},
};
use core::{
fmt::{self, Debug, Formatter},
ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
};
use num_traits::{
CheckedAdd, One, Zero,
bounds::{LowerBounded, UpperBounded},
};
#[derive(Clone, Copy, PartialEq, Eq, derive_more::Display)]
#[display("{start}..={end}")]
pub struct Interval<T> {
start: T,
end: T,
}
impl<T: Numerated> Interval<T> {
#[track_caller]
pub unsafe fn new_unchecked(start: T, end: T) -> Self {
debug_assert!(
start <= end,
"Calling this method you must guarantee that `start ≤ end`"
);
Self { start, end }
}
pub fn new(start: T, end: T) -> Option<Self> {
(start <= end).then_some(Self { start, end })
}
pub fn start(&self) -> T {
self.start
}
pub fn end(&self) -> T {
self.end
}
pub fn iter(&self) -> IntervalIterator<T> {
(*self).into()
}
pub fn into_parts(self) -> (T, T) {
self.into()
}
pub fn inc_start(&self) -> Option<Self> {
let (start, end) = (self.start, self.end);
debug_assert!(start <= end, "It's guaranteed by `Interval`");
start.inc_if_lt(end).map(|start| {
debug_assert!(start <= end, "`T: Numerated` impl error");
Interval { start, end }
})
}
pub fn try_from_range(range: Range<T>) -> Result<Self, TryFromRangeError> {
let (start, end) = (range.start, range.end);
end.dec_if_gt(start)
.map(|end| {
debug_assert!(start <= end, "`T: Numerated` impl error");
Self { start, end }
})
.ok_or(if start == end {
TryFromRangeError::EmptyRange
} else {
TryFromRangeError::IncorrectRange
})
}
}
impl<T: Numerated> PartialEq<RangeInclusive<T>> for Interval<T> {
fn eq(&self, other: &RangeInclusive<T>) -> bool {
let (start, end) = self.into_parts();
(start, end) == (*other.start(), *other.end())
}
}
impl<T: Numerated> From<Interval<T>> for (T, T) {
fn from(interval: Interval<T>) -> (T, T) {
(interval.start, interval.end)
}
}
impl<T: Numerated> From<Interval<T>> for RangeInclusive<T> {
fn from(interval: Interval<T>) -> Self {
interval.start..=interval.end
}
}
impl<T: Numerated> From<T> for Interval<T> {
fn from(point: T) -> Self {
let (start, end) = (point, point);
debug_assert!(start <= end, "`T: Ord` impl error");
Self { start, end }
}
}
impl<T: Numerated + LowerBounded> From<RangeToInclusive<T>> for Interval<T> {
fn from(range: RangeToInclusive<T>) -> Self {
let (start, end) = (T::min_value(), range.end);
debug_assert!(start <= end, "`T: LowerBounded` impl error");
Self { start, end }
}
}
impl<T: Numerated + UpperBounded + LowerBounded> From<RangeFull> for Interval<T> {
fn from(_: RangeFull) -> Self {
let (start, end) = (T::min_value(), T::max_value());
debug_assert!(start <= end, "`T: UpperBounded + LowerBounded` impl error");
Self { start, end }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EmptyRangeError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IncorrectRangeError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TryFromRangeError {
EmptyRange,
IncorrectRange,
}
impl<T: Numerated + UpperBounded, I: Into<T::Bound>> TryFrom<RangeFrom<I>> for Interval<T> {
type Error = EmptyRangeError;
fn try_from(range: RangeFrom<I>) -> Result<Self, Self::Error> {
match Into::<T::Bound>::into(range.start).unbound() {
Some(start) => {
let end = T::max_value();
debug_assert!(start <= end, "`T: UpperBounded` impl error");
Ok(Self { start, end })
}
None => Err(EmptyRangeError),
}
}
}
impl<T: Numerated + LowerBounded + UpperBounded, I: Into<T::Bound>> TryFrom<RangeTo<I>>
for Interval<T>
{
type Error = EmptyRangeError;
fn try_from(range: RangeTo<I>) -> Result<Self, Self::Error> {
let end: T::Bound = range.end.into();
let Some(end) = end.unbound() else {
return Ok(Self::from(..));
};
let start = T::min_value();
end.dec_if_gt(start)
.map(|end| {
debug_assert!(start <= end, "`T: LowerBounded` impl error");
Self { start, end }
})
.ok_or(EmptyRangeError)
}
}
impl<T: Numerated> TryFrom<RangeInclusive<T>> for Interval<T> {
type Error = IncorrectRangeError;
fn try_from(range: RangeInclusive<T>) -> Result<Self, Self::Error> {
let (start, end) = range.into_inner();
(start <= end)
.then_some(Self { start, end })
.ok_or(IncorrectRangeError)
}
}
impl<T, S, E> TryFrom<(S, E)> for Interval<T>
where
T: Numerated + UpperBounded,
S: Into<T::Bound>,
E: Into<T::Bound>,
{
type Error = TryFromRangeError;
fn try_from((start, end): (S, E)) -> Result<Self, Self::Error> {
let start: T::Bound = start.into();
let end: T::Bound = end.into();
match (start.unbound(), end.unbound()) {
(None, None) => Err(TryFromRangeError::EmptyRange),
(None, Some(_)) => Err(TryFromRangeError::IncorrectRange),
(start, None) => Self::try_from(start..).map_err(|_| TryFromRangeError::EmptyRange),
(Some(start), Some(end)) => Self::try_from_range(start..end),
}
}
}
impl<T: Numerated + UpperBounded, I: Into<T::Bound>> TryFrom<Range<I>> for Interval<T> {
type Error = TryFromRangeError;
fn try_from(range: Range<I>) -> Result<Self, Self::Error> {
Self::try_from((range.start, range.end))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NewWithLenError {
ZeroLen,
OutOfBounds,
}
impl<T: Numerated + UpperBounded> Interval<T> {
pub fn with_len<S: Into<T::Bound>, L: Into<Option<T::Distance>>>(
start: S,
len: L,
) -> Result<Interval<T>, NewWithLenError> {
let start: T::Bound = start.into();
let len: Option<T::Distance> = len.into();
match (start.unbound(), len) {
(_, Some(len)) if len.is_zero() => Err(NewWithLenError::ZeroLen),
(None, _) => Err(NewWithLenError::OutOfBounds),
(Some(start), len) => {
let distance = len
.map(|len| len - T::Distance::one())
.unwrap_or(T::Distance::max_value());
start
.add_if_enclosed_by(distance, T::max_value())
.map(|end| {
debug_assert!(start <= end, "`T: Numerated` impl error");
Self { start, end }
})
.ok_or(NewWithLenError::OutOfBounds)
}
}
}
}
impl<T: Numerated> Interval<T> {
pub fn raw_len(&self) -> Option<T::Distance> {
let (start, end) = self.into_parts();
end.distance(start).checked_add(&T::Distance::one())
}
}
impl<T: Numerated + LowerBounded + UpperBounded> Interval<T> {
pub fn len(&self) -> T::Bound {
self.raw_len()
.and_then(|raw_len| T::min_value().add_if_enclosed_by(raw_len, T::max_value()))
.into()
}
}
impl<T: Debug> Debug for Interval<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:?}..={:?}", self.start, self.end)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn len() {
assert_eq!(Interval::<u8>::try_from(1..7).unwrap().len(), 6);
assert_eq!(Interval::<u8>::try_from(..1).unwrap().len(), 1);
assert_eq!(Interval::<u8>::from(..=1).len(), 2);
assert_eq!(Interval::<u8>::try_from(1..).unwrap().len(), 255);
assert_eq!(Interval::<u8>::try_from(0..).unwrap().len(), None);
assert_eq!(Interval::<u8>::from(..).len(), None);
assert_eq!(Interval::<u8>::try_from(1..7).unwrap().raw_len(), Some(6));
assert_eq!(Interval::<u8>::try_from(..1).unwrap().raw_len(), Some(1));
assert_eq!(Interval::<u8>::from(..=1).raw_len(), Some(2));
assert_eq!(Interval::<u8>::try_from(1..).unwrap().raw_len(), Some(255));
assert_eq!(Interval::<u8>::try_from(0..).unwrap().raw_len(), None);
assert_eq!(Interval::<u8>::from(..).raw_len(), None);
assert_eq!(Interval::<i8>::try_from(-1..1).unwrap().len(), -126); assert_eq!(Interval::<i8>::from(..=1).len(), 2); assert_eq!(Interval::<i8>::try_from(..1).unwrap().len(), 1); assert_eq!(Interval::<i8>::try_from(1..).unwrap().len(), -1); assert_eq!(Interval::<i8>::from(..).len(), None);
assert_eq!(Interval::<i8>::try_from(-1..1).unwrap().raw_len(), Some(2));
assert_eq!(Interval::<i8>::try_from(..1).unwrap().raw_len(), Some(129));
assert_eq!(Interval::<i8>::from(..=1).raw_len(), Some(130));
assert_eq!(Interval::<i8>::try_from(1..).unwrap().raw_len(), Some(127));
assert_eq!(Interval::<i8>::from(..).raw_len(), None);
}
#[test]
fn count_from() {
assert_eq!(Interval::<u8>::with_len(0, 100).unwrap(), 0..=99);
assert_eq!(Interval::<u8>::with_len(0, 255).unwrap(), 0..=254);
assert_eq!(Interval::<u8>::with_len(0, None).unwrap(), 0..=255);
assert_eq!(Interval::<u8>::with_len(1, 255).unwrap(), 1..=255);
assert_eq!(Interval::<u8>::with_len(0, 1).unwrap(), 0..=0);
}
}