#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct KeyRange {
start: u64,
end: u64,
}
impl KeyRange {
#[inline]
#[must_use]
pub const fn new(start: u64, end: u64) -> Option<Self> {
if start <= end {
Some(Self { start, end })
} else {
None
}
}
#[inline]
#[must_use]
pub const fn point(key: u64) -> Self {
Self {
start: key,
end: key,
}
}
#[inline]
#[must_use]
pub const fn start(self) -> u64 {
self.start
}
#[inline]
#[must_use]
pub const fn end(self) -> u64 {
self.end
}
#[inline]
#[must_use]
pub const fn contains(self, key: u64) -> bool {
self.start <= key && key <= self.end
}
#[inline]
#[must_use]
pub const fn overlaps(self, other: KeyRange) -> bool {
self.start <= other.end && other.start <= self.end
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::KeyRange;
#[test]
fn test_new_rejects_inverted_range() {
assert!(KeyRange::new(5, 4).is_none());
assert!(KeyRange::new(4, 4).is_some());
assert!(KeyRange::new(4, 5).is_some());
}
#[test]
fn test_point_is_single_key() {
let p = KeyRange::point(7);
assert_eq!((p.start(), p.end()), (7, 7));
assert!(p.contains(7));
assert!(!p.contains(6));
assert!(!p.contains(8));
}
#[test]
fn test_contains_bounds_inclusive() {
let r = KeyRange::new(10, 20).unwrap();
assert!(r.contains(10));
assert!(r.contains(15));
assert!(r.contains(20));
assert!(!r.contains(9));
assert!(!r.contains(21));
}
#[test]
fn test_overlap_is_symmetric() {
let a = KeyRange::new(10, 20).unwrap();
let cases = [
KeyRange::new(0, 9).unwrap(),
KeyRange::new(0, 10).unwrap(),
KeyRange::new(15, 25).unwrap(),
KeyRange::new(20, 30).unwrap(),
KeyRange::new(21, 30).unwrap(),
KeyRange::new(5, 25).unwrap(),
];
for b in cases {
assert_eq!(a.overlaps(b), b.overlaps(a), "{a:?} vs {b:?}");
}
}
#[test]
fn test_adjacent_ranges_overlap_at_shared_bound() {
assert!(
KeyRange::new(10, 20)
.unwrap()
.overlaps(KeyRange::new(20, 30).unwrap())
);
assert!(
!KeyRange::new(10, 20)
.unwrap()
.overlaps(KeyRange::new(21, 30).unwrap())
);
}
#[test]
fn test_contained_range_overlaps() {
let outer = KeyRange::new(0, 100).unwrap();
let inner = KeyRange::new(40, 60).unwrap();
assert!(outer.overlaps(inner));
assert!(inner.overlaps(outer));
}
#[test]
fn test_max_key_has_no_overflow() {
let r = KeyRange::point(u64::MAX);
assert!(r.contains(u64::MAX));
assert!(r.overlaps(KeyRange::new(0, u64::MAX).unwrap()));
}
}