use ehttpd::{error, error::Error};
use std::{
cmp::Ordering,
ops::{Bound, RangeBounds, RangeInclusive},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AnyInclusiveRange<T> {
Full,
From {
start: T,
},
To {
end: T,
},
FromTo {
start: T,
end: T,
},
}
impl<T> AnyInclusiveRange<T> {
pub fn to_inclusive(&self, start_incl: T, end_incl: T) -> Result<RangeInclusive<T>, Error>
where
T: PartialOrd + Copy,
{
let start_incl = match self {
Self::Full | Self::To { .. } => start_incl,
Self::From { start } => *start,
Self::FromTo { start, .. } => *start,
};
let end_incl = match self {
Self::Full | Self::From { .. } => end_incl,
Self::To { end } => *end,
Self::FromTo { end, .. } => *end,
};
match start_incl.partial_cmp(&end_incl) {
Some(Ordering::Less | Ordering::Equal) => Ok(start_incl..=end_incl),
_ => Err(error!("End of inclusive range is before start")),
}
}
pub fn convert<U, E>(self) -> Result<AnyInclusiveRange<U>, E>
where
T: TryInto<U>,
E: From<<T as TryInto<U>>::Error>,
{
let range = match self {
Self::Full => AnyInclusiveRange::Full,
Self::From { start } => AnyInclusiveRange::From { start: start.try_into()? },
Self::To { end } => AnyInclusiveRange::To { end: end.try_into()? },
Self::FromTo { start, end } => AnyInclusiveRange::FromTo { start: start.try_into()?, end: end.try_into()? },
};
Ok(range)
}
}
impl<T> RangeBounds<T> for AnyInclusiveRange<T> {
fn start_bound(&self) -> Bound<&T> {
match self {
Self::Full | Self::To { .. } => Bound::Unbounded,
Self::From { start } | Self::FromTo { start, .. } => Bound::Included(start),
}
}
fn end_bound(&self) -> Bound<&T> {
match self {
AnyInclusiveRange::Full | AnyInclusiveRange::From { .. } => Bound::Unbounded,
AnyInclusiveRange::To { end } | AnyInclusiveRange::FromTo { end, .. } => Bound::Included(end),
}
}
}