use std::fmt;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct SlotRange {
start: u64,
end: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("invalid range: start_slot {start} > end_slot {end}")]
pub struct InvertedSlotRange {
pub start: u64,
pub end: u64,
}
impl SlotRange {
pub fn new(start: u64, end: u64) -> Result<Self, InvertedSlotRange> {
if start > end {
return Err(InvertedSlotRange { start, end });
}
Ok(Self { start, end })
}
pub const fn new_unchecked(start: u64, end: u64) -> Self {
Self { start, end }
}
pub const fn from_count(start: u64, count: u64) -> Self {
Self {
start,
end: start.saturating_add(count.saturating_sub(1)),
}
}
pub const fn start(&self) -> u64 {
self.start
}
pub const fn end(&self) -> u64 {
self.end
}
pub const fn len(&self) -> u64 {
self.end - self.start + 1
}
pub const fn is_empty(&self) -> bool {
false
}
pub const fn contains(&self, slot: u64) -> bool {
slot >= self.start && slot <= self.end
}
}
impl fmt::Display for SlotRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}..={}", self.start, self.end)
}
}
impl From<SlotRange> for std::ops::RangeInclusive<u64> {
fn from(range: SlotRange) -> Self {
range.start..=range.end
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_inverted_range_is_rejected() {
assert_eq!(
SlotRange::new(10, 9),
Err(InvertedSlotRange { start: 10, end: 9 })
);
assert!(SlotRange::new(10, 10).is_ok());
}
#[test]
fn length_is_inclusive_of_both_bounds() {
assert_eq!(SlotRange::new(10, 10).expect("range").len(), 1);
assert_eq!(SlotRange::new(10, 19).expect("range").len(), 10);
}
#[test]
fn from_count_matches_the_start_plus_count_convention() {
assert_eq!(
SlotRange::from_count(100, 1),
SlotRange::new_unchecked(100, 100)
);
assert_eq!(
SlotRange::from_count(100, 10),
SlotRange::new_unchecked(100, 109)
);
assert_eq!(
SlotRange::from_count(100, 0),
SlotRange::new_unchecked(100, 100)
);
}
#[test]
fn contains_is_inclusive() {
let range = SlotRange::new(10, 12).expect("range");
assert!(range.contains(10) && range.contains(12));
assert!(!range.contains(9) && !range.contains(13));
}
}