use core::ops::{Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("invalid size hint: values describe an invalid or empty range")]
pub struct InvalidSizeHint;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[readonly::make]
pub struct SizeHint {
pub lower: usize,
pub upper: Option<usize>,
}
impl SizeHint {
pub const UNIVERSAL: Self = Self { lower: 0, upper: None };
pub const ZERO: Self = Self { lower: 0, upper: Some(0) };
#[inline]
#[must_use]
pub const fn new(lower: usize, upper: Option<usize>) -> Self {
match Self::try_new(lower, upper) {
Ok(hint) => hint,
Err(_) => panic!("values should describe a valid size hint"),
}
}
#[inline]
pub const fn try_new(lower: usize, upper: Option<usize>) -> Result<Self, InvalidSizeHint> {
match (lower, upper) {
(lower, Some(upper)) if lower > upper => Err(InvalidSizeHint),
_ => Ok(Self { lower, upper }),
}
}
#[inline]
#[must_use]
pub const fn bounded(lower: usize, upper: usize) -> Self {
match Self::try_bounded(lower, upper) {
Ok(hint) => hint,
Err(_) => panic!("values should describe a valid size hint"),
}
}
#[inline]
pub const fn try_bounded(lower: usize, upper: usize) -> Result<Self, InvalidSizeHint> {
match lower > upper {
true => Err(InvalidSizeHint),
false => Ok(Self { lower, upper: Some(upper) }),
}
}
#[inline]
#[must_use]
pub const fn unbounded(lower: usize) -> Self {
Self { lower, upper: None }
}
#[inline]
#[must_use]
pub const fn exact(len: usize) -> Self {
Self { lower: len, upper: Some(len) }
}
#[inline]
#[must_use]
pub const fn at_most(upper: usize) -> Self {
Self { lower: 0, upper: Some(upper) }
}
#[inline]
#[must_use]
pub const fn lower(self) -> usize {
self.lower
}
#[inline]
#[must_use]
pub const fn upper(self) -> Option<usize> {
self.upper
}
#[inline]
#[must_use]
pub const fn as_hint(self) -> (usize, Option<usize>) {
(self.lower, self.upper)
}
#[inline]
#[must_use]
pub fn decrement(self) -> Self {
Self { lower: self.lower.saturating_sub(1), upper: self.upper.map(|upper| upper.saturating_sub(1)) }
}
#[inline]
#[must_use]
pub const fn overlaps(self, other: Self) -> bool {
match (self.as_hint(), other.as_hint()) {
((a_lower, Some(a_upper)), (b_lower, Some(b_upper))) => a_lower <= b_upper && b_lower <= a_upper,
((_, Some(a_upper)), (b_lower, None)) => a_upper >= b_lower,
((a_lower, None), (_, Some(b_upper))) => b_upper >= a_lower,
((_, None), (_, None)) => true,
}
}
#[inline]
#[must_use]
pub const fn disjoint(self, other: Self) -> bool {
!Self::overlaps(self, other)
}
#[inline]
#[must_use]
pub const fn subset_of(self, other: Self) -> bool {
match (self.as_hint(), other.as_hint()) {
((a_low, Some(a_up)), (b_low, Some(b_up))) => a_low >= b_low && a_up <= b_up,
((a_low, _), (b_low, None)) => a_low >= b_low,
((_, None), (_, Some(_))) => false,
}
}
}
impl TryFrom<(usize, Option<usize>)> for SizeHint {
type Error = InvalidSizeHint;
#[inline]
fn try_from(hint: (usize, Option<usize>)) -> Result<Self, Self::Error> {
match hint {
(lower, Some(upper)) => Self::try_bounded(lower, upper),
(lower, None) => Ok(Self::unbounded(lower)),
}
}
}
impl From<SizeHint> for (usize, Option<usize>) {
#[inline]
fn from(hint: SizeHint) -> Self {
(hint.lower, hint.upper)
}
}
impl TryFrom<Range<usize>> for SizeHint {
type Error = InvalidSizeHint;
#[inline]
fn try_from(range: Range<usize>) -> Result<Self, Self::Error> {
let end = range.end.checked_sub(1).ok_or(InvalidSizeHint)?;
Self::try_bounded(range.start, end)
}
}
impl TryFrom<RangeInclusive<usize>> for SizeHint {
type Error = InvalidSizeHint;
#[inline]
fn try_from(range: RangeInclusive<usize>) -> Result<Self, Self::Error> {
Self::try_bounded(*range.start(), *range.end())
}
}
impl From<RangeFull> for SizeHint {
#[inline]
fn from(_: RangeFull) -> Self {
Self::UNIVERSAL
}
}
impl From<RangeFrom<usize>> for SizeHint {
#[inline]
fn from(range: RangeFrom<usize>) -> Self {
Self { lower: range.start, upper: None }
}
}
impl TryFrom<RangeTo<usize>> for SizeHint {
type Error = InvalidSizeHint;
#[inline]
fn try_from(range: RangeTo<usize>) -> Result<Self, Self::Error> {
let end = range.end.checked_sub(1).ok_or(InvalidSizeHint)?;
Self::try_bounded(0, end)
}
}
impl From<RangeToInclusive<usize>> for SizeHint {
#[inline]
fn from(range: RangeToInclusive<usize>) -> Self {
Self { lower: 0, upper: Some(range.end) }
}
}
impl RangeBounds<usize> for SizeHint {
#[inline]
fn start_bound(&self) -> Bound<&usize> {
Bound::Included(&self.lower)
}
#[inline]
fn end_bound(&self) -> Bound<&usize> {
self.upper.as_ref().map_or(Bound::Unbounded, Bound::Included)
}
}
impl PartialEq<(usize, Option<usize>)> for SizeHint {
fn eq(&self, other: &(usize, Option<usize>)) -> bool {
self.lower == other.0 && self.upper == other.1
}
}
impl PartialEq<SizeHint> for (usize, Option<usize>) {
fn eq(&self, other: &SizeHint) -> bool {
self.0 == other.lower && self.1 == other.upper
}
}