ehttpd_range/
anyrange.rs

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