use std::ops::{Add, Bound, RangeBounds};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct LengthBound {
minimum: u16,
maximum: u16,
}
impl Default for LengthBound {
fn default() -> Self {
LengthBound {
minimum: u16::MIN,
maximum: u16::MAX,
}
}
}
impl Add for LengthBound {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let minimum = self.minimum.saturating_add(rhs.minimum);
let maximum = if self.maximum < u16::MAX && rhs.maximum < u16::MAX {
self.maximum.saturating_add(rhs.maximum)
} else {
u16::MAX
};
let maximum = maximum.max(minimum);
LengthBound { minimum, maximum }
}
}
impl LengthBound {
pub fn new<R>(bounds: R) -> Self
where
R: RangeBounds<u16>,
{
let minimum = min_bound(bounds.start_bound());
let maximum = max_bound(bounds.end_bound());
LengthBound { minimum, maximum }
}
pub fn minimum(self) -> u16 {
self.minimum
}
pub fn maximum(self) -> u16 {
self.maximum
}
pub fn available(self) -> u16 {
self.maximum - self.minimum
}
pub fn increase(&mut self, amount: u16) {
self.minimum += amount;
self.maximum = self.maximum.max(self.minimum);
}
pub fn decrease(&mut self, amount: u16) {
self.maximum = amount.max(self.minimum);
}
}
fn min_bound(bound: Bound<&u16>) -> u16 {
match bound {
Bound::Unbounded => u16::MIN,
Bound::Included(x) => *x,
Bound::Excluded(x) => (*x).saturating_add(1),
}
}
fn max_bound(bound: Bound<&u16>) -> u16 {
match bound {
Bound::Unbounded => u16::MAX,
Bound::Included(x) => (*x).saturating_add(1),
Bound::Excluded(x) => *x,
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn bounds() {
let bnd = LengthBound::default();
assert_eq!(bnd.minimum, 0);
assert_eq!(bnd.maximum, u16::MAX);
let bnd = LengthBound::new(..5);
assert_eq!(bnd.minimum, 0);
assert_eq!(bnd.maximum, 5);
let bnd = LengthBound::new(2..=2);
assert_eq!(bnd.minimum, 2);
assert_eq!(bnd.maximum, 3);
}
}