diff --git a/avbroot/src/util.rs b/avbroot/src/util.rs index e2db1ad..2d984a3 100644 --- a/avbroot/src/util.rs +++ b/avbroot/src/util.rs @@ -4,7 +4,9 @@ use std::{ cmp::Ordering, fmt, mem, - ops::{Bound, Range, RangeBounds}, + ops::{ + Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, + }, path::Path, }; @@ -43,59 +45,118 @@ impl fmt::Debug for DebugString { } } -/// A single bound in a range. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum IntBound { - Included(T), - Excluded(T), +#[derive(Clone, Hash, PartialEq, Eq)] +pub enum AnyRange { + Range(Range), + RangeFrom(RangeFrom), + RangeFull(RangeFull), + RangeInclusive(RangeInclusive), + RangeTo(RangeTo), + RangeToInclusive(RangeToInclusive), } -/// A bounded primitive integer range. Unlike std's range types, this is a -/// single type that can represent open, closed, and half-open intervals. -#[derive(Clone, Copy, Debug)] -pub struct IntRange { - pub start: IntBound, - pub end: IntBound, -} - -impl IntRange { - /// Returns [`None`] if the range bounds cannot be represented by `T`. If - /// the start and end of `range` are unbounded, then it gets converted to - /// [`IntBound::Included`] with `N`'s minimum or maximum value. - pub fn new>(range: R) -> Option { - let start = match range.start_bound() { - Bound::Included(n) => IntBound::Included(T::from(*n)?), - Bound::Excluded(n) => IntBound::Excluded(T::from(*n)?), - Bound::Unbounded => IntBound::Included(T::from(N::min_value())?), +impl AnyRange { + pub fn with_bounds(start: Bound, end: Bound) -> Option { + let result = match (start, end) { + (Bound::Included(s), Bound::Excluded(e)) => Self::Range(s..e), + (Bound::Included(s), Bound::Unbounded) => Self::RangeFrom(s..), + (Bound::Unbounded, Bound::Unbounded) => Self::RangeFull(..), + (Bound::Included(s), Bound::Included(e)) => Self::RangeInclusive(s..=e), + (Bound::Unbounded, Bound::Excluded(e)) => Self::RangeTo(..e), + (Bound::Unbounded, Bound::Included(e)) => Self::RangeToInclusive(..=e), + (Bound::Excluded(_), _) => return None, }; - let end = match range.end_bound() { - Bound::Included(n) => IntBound::Included(T::from(*n)?), - Bound::Excluded(n) => IntBound::Excluded(T::from(*n)?), - Bound::Unbounded => IntBound::Included(T::from(N::max_value())?), - }; - - Some(Self { start, end }) + Some(result) } } -impl fmt::Display for IntRange { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.start { - IntBound::Included(n) => write!(f, "[{n}, ")?, - IntBound::Excluded(n) => write!(f, "({n}, ")?, - } +impl> AnyRange { + pub fn contains(&self, item: &U) -> bool + where + T: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } +} - match self.end { - IntBound::Included(n) => write!(f, "{n}]"), - IntBound::Excluded(n) => write!(f, "{n})"), +impl fmt::Debug for AnyRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Range(r) => r.fmt(f), + Self::RangeFrom(r) => r.fmt(f), + Self::RangeFull(r) => r.fmt(f), + Self::RangeInclusive(r) => r.fmt(f), + Self::RangeTo(r) => r.fmt(f), + Self::RangeToInclusive(r) => r.fmt(f), } } } +impl RangeBounds for AnyRange { + fn start_bound(&self) -> Bound<&T> { + match self { + Self::Range(r) => r.start_bound(), + Self::RangeFrom(r) => r.start_bound(), + Self::RangeFull(r) => r.start_bound(), + Self::RangeInclusive(r) => r.start_bound(), + Self::RangeTo(r) => r.start_bound(), + Self::RangeToInclusive(r) => r.start_bound(), + } + } + + fn end_bound(&self) -> Bound<&T> { + match self { + Self::Range(r) => r.end_bound(), + Self::RangeFrom(r) => r.end_bound(), + Self::RangeFull(r) => r.end_bound(), + Self::RangeInclusive(r) => r.end_bound(), + Self::RangeTo(r) => r.end_bound(), + Self::RangeToInclusive(r) => r.end_bound(), + } + } +} + +impl From> for AnyRange { + fn from(value: Range) -> Self { + Self::Range(value) + } +} + +impl From> for AnyRange { + fn from(value: RangeFrom) -> Self { + Self::RangeFrom(value) + } +} + +impl From for AnyRange { + fn from(value: RangeFull) -> Self { + Self::RangeFull(value) + } +} + +impl From> for AnyRange { + fn from(value: RangeInclusive) -> Self { + Self::RangeInclusive(value) + } +} + +impl From> for AnyRange { + fn from(value: RangeTo) -> Self { + Self::RangeTo(value) + } +} + +impl From> for AnyRange { + fn from(value: RangeToInclusive) -> Self { + Self::RangeToInclusive(value) + } +} + /// A non-generic type that can represent any 64-bit or smaller primitive /// integer. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum LargeInt { Signed(i64), Unsigned(u64), @@ -112,13 +173,13 @@ impl fmt::Display for LargeInt { /// A non-generic type that can represent any 64-bit or smaller primitive /// integer range. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, PartialEq, Eq)] pub enum LargeIntRange { - Signed(IntRange), - Unsigned(IntRange), + Signed(AnyRange), + Unsigned(AnyRange), } -impl fmt::Display for LargeIntRange { +impl fmt::Debug for LargeIntRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Signed(r) => r.fmt(f), @@ -128,8 +189,8 @@ impl fmt::Display for LargeIntRange { } /// An error returned when a value is not within a specific range. -#[derive(Clone, Copy, Debug, Error)] -#[error("Integer value {value} not in bounds: {range}")] +#[derive(Clone, Debug, Error)] +#[error("Integer value {value} not in bounds: {range:?}")] pub struct OutOfBoundsError { value: LargeInt, range: LargeIntRange, @@ -138,7 +199,7 @@ pub struct OutOfBoundsError { /// Verify that `value` is within `bounds` and then return `value` if it is. pub fn check_bounds( value: T, - bounds: impl RangeBounds, + range: impl Into>, ) -> Result { const { assert!( @@ -147,7 +208,9 @@ pub fn check_bounds( ); } - if !bounds.contains(&value) { + let range = range.into(); + + if !range.contains(&value) { let value = if T::min_value() != T::zero() { LargeInt::Signed(NumCast::from(value).unwrap()) } else { @@ -155,9 +218,33 @@ pub fn check_bounds( }; let range = if T::min_value() != T::zero() { - LargeIntRange::Signed(IntRange::new(bounds).unwrap()) + let start = match range.start_bound() { + Bound::Excluded(n) => Bound::Excluded(NumCast::from(*n).unwrap()), + Bound::Included(n) => Bound::Included(NumCast::from(*n).unwrap()), + Bound::Unbounded => Bound::Unbounded, + }; + + let end = match range.end_bound() { + Bound::Excluded(n) => Bound::Excluded(NumCast::from(*n).unwrap()), + Bound::Included(n) => Bound::Included(NumCast::from(*n).unwrap()), + Bound::Unbounded => Bound::Unbounded, + }; + + LargeIntRange::Signed(AnyRange::with_bounds(start, end).unwrap()) } else { - LargeIntRange::Unsigned(IntRange::new(bounds).unwrap()) + let start = match range.start_bound() { + Bound::Excluded(n) => Bound::Excluded(NumCast::from(*n).unwrap()), + Bound::Included(n) => Bound::Included(NumCast::from(*n).unwrap()), + Bound::Unbounded => Bound::Unbounded, + }; + + let end = match range.end_bound() { + Bound::Excluded(n) => Bound::Excluded(NumCast::from(*n).unwrap()), + Bound::Included(n) => Bound::Included(NumCast::from(*n).unwrap()), + Bound::Unbounded => Bound::Unbounded, + }; + + LargeIntRange::Unsigned(AnyRange::with_bounds(start, end).unwrap()) }; return Err(OutOfBoundsError { value, range }); @@ -184,9 +271,15 @@ pub fn try_cast(value: V) -> Result }; let range = if T::min_value() != T::zero() { - LargeIntRange::Signed(IntRange::new::(..).unwrap()) + let min = NumCast::from(T::min_value()).unwrap(); + let max = NumCast::from(T::max_value()).unwrap(); + + LargeIntRange::Signed((min..=max).into()) } else { - LargeIntRange::Unsigned(IntRange::new::(..).unwrap()) + let min = NumCast::from(T::min_value()).unwrap(); + let max = NumCast::from(T::max_value()).unwrap(); + + LargeIntRange::Unsigned((min..=max).into()) }; OutOfBoundsError { value, range } @@ -287,21 +380,27 @@ where #[cfg(test)] mod tests { - use assert_matches::assert_matches; - use super::*; #[test] - fn test_int_range() { - let range = IntRange::new::(..).unwrap(); - assert_eq!(range.start, IntBound::Included(u8::MIN)); - assert_eq!(range.end, IntBound::Included(u8::MAX)); + fn test_any_range() { + let range = AnyRange::with_bounds(Bound::Included(0), Bound::Excluded(1)).unwrap(); + assert_eq!(range, AnyRange::from(0..1)); - let range = IntRange::::new(-2i16..2i16).unwrap(); - assert_eq!(range.start, IntBound::Included(-2)); - assert_eq!(range.end, IntBound::Excluded(2)); + let range = AnyRange::with_bounds(Bound::Included(0), Bound::Unbounded).unwrap(); + assert_eq!(range, AnyRange::from(0..)); - assert!(IntRange::::new::(..).is_none()); + let range = AnyRange::::with_bounds(Bound::Unbounded, Bound::Unbounded).unwrap(); + assert_eq!(range, AnyRange::from(..)); + + let range = AnyRange::with_bounds(Bound::Included(0), Bound::Included(1)).unwrap(); + assert_eq!(range, AnyRange::from(0..=1)); + + let range = AnyRange::with_bounds(Bound::Unbounded, Bound::Excluded(1)).unwrap(); + assert_eq!(range, AnyRange::from(..1)); + + let range = AnyRange::with_bounds(Bound::Unbounded, Bound::Included(1)).unwrap(); + assert_eq!(range, AnyRange::from(..=1)); } #[test] @@ -313,24 +412,12 @@ mod tests { check_bounds(0, -1..=1).unwrap(); let err = check_bounds(i8::MAX, 0..=0).unwrap_err(); - assert_matches!(err.value, LargeInt::Signed(127)); - assert_matches!( - err.range, - LargeIntRange::Signed(IntRange { - start: IntBound::Included(0), - end: IntBound::Included(0), - }) - ); + assert_eq!(err.value, LargeInt::Signed(127)); + assert_eq!(err.range, LargeIntRange::Signed(AnyRange::from(0..=0))); let err = check_bounds(u8::MAX, 0..=0).unwrap_err(); - assert_matches!(err.value, LargeInt::Unsigned(255)); - assert_matches!( - err.range, - LargeIntRange::Unsigned(IntRange { - start: IntBound::Included(0), - end: IntBound::Included(0), - }) - ); + assert_eq!(err.value, LargeInt::Unsigned(255)); + assert_eq!(err.range, LargeIntRange::Unsigned(AnyRange::from(0..=0))); } #[test] @@ -339,14 +426,8 @@ mod tests { assert_eq!(value, 255); let err = try_cast::(256u16).unwrap_err(); - assert_matches!(err.value, LargeInt::Unsigned(256)); - assert_matches!( - err.range, - LargeIntRange::Signed(IntRange { - start: IntBound::Included(-128), - end: IntBound::Included(127), - }) - ); + assert_eq!(err.value, LargeInt::Unsigned(256)); + assert_eq!(err.range, LargeIntRange::Signed(AnyRange::from(-128..=127))); } #[test]