Skip to main content

ehttpd_range/
anyrange.rs

1//! A type-erased inclusive range container
2
3use ehttpd::err;
4use ehttpd::error::Error;
5use std::cmp::Ordering;
6use std::ops::{Bound, RangeBounds, RangeInclusive};
7
8/// A type-erased inclusive range
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum AnyInclusiveRange<T> {
11    /// An unbounded/open range
12    Full,
13    /// A range with a lower inclusive boundary
14    From {
15        /// The first element (inclusive)
16        start: T,
17    },
18    /// A range with an upper inclusive boundary
19    To {
20        /// The last element (inclusive)
21        end: T,
22    },
23    /// A range with a lower and upper inclusive boundary
24    FromTo {
25        /// The first element (inclusive)
26        start: T,
27        /// The last element (inclusive)
28        end: T,
29    },
30}
31impl<T> AnyInclusiveRange<T> {
32    /// Creates an inclusive range from `self`, replacing unspecified boundaries with the given boundaries if necessary
33    pub fn to_inclusive(&self, start_incl: T, end_incl: T) -> Result<RangeInclusive<T>, Error>
34    where
35        T: PartialOrd + Copy,
36    {
37        // Take or specify boundaries
38        let start_incl = match self {
39            Self::Full | Self::To { .. } => start_incl,
40            Self::From { start } => *start,
41            Self::FromTo { start, .. } => *start,
42        };
43        let end_incl = match self {
44            Self::Full | Self::From { .. } => end_incl,
45            Self::To { end } => *end,
46            Self::FromTo { end, .. } => *end,
47        };
48
49        // Validate resulting range
50        match start_incl.partial_cmp(&end_incl) {
51            Some(Ordering::Less | Ordering::Equal) => Ok(start_incl..=end_incl),
52            _ => Err(err!("End of inclusive range is before start")),
53        }
54    }
55
56    /// Converts the range into a different type
57    pub fn convert<U, E>(self) -> Result<AnyInclusiveRange<U>, E>
58    where
59        T: TryInto<U>,
60        E: From<<T as TryInto<U>>::Error>,
61    {
62        let range = match self {
63            Self::Full => AnyInclusiveRange::Full,
64            Self::From { start } => AnyInclusiveRange::From { start: start.try_into()? },
65            Self::To { end } => AnyInclusiveRange::To { end: end.try_into()? },
66            Self::FromTo { start, end } => AnyInclusiveRange::FromTo { start: start.try_into()?, end: end.try_into()? },
67        };
68        Ok(range)
69    }
70}
71impl<T> RangeBounds<T> for AnyInclusiveRange<T> {
72    fn start_bound(&self) -> Bound<&T> {
73        match self {
74            Self::Full | Self::To { .. } => Bound::Unbounded,
75            Self::From { start } | Self::FromTo { start, .. } => Bound::Included(start),
76        }
77    }
78    fn end_bound(&self) -> Bound<&T> {
79        match self {
80            AnyInclusiveRange::Full | AnyInclusiveRange::From { .. } => Bound::Unbounded,
81            AnyInclusiveRange::To { end } | AnyInclusiveRange::FromTo { end, .. } => Bound::Included(end),
82        }
83    }
84}