simulator_api/
slot_range.rs1use std::fmt;
9
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
16pub struct SlotRange {
17 start: u64,
18 end: u64,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
23#[error("invalid range: start_slot {start} > end_slot {end}")]
24pub struct InvertedSlotRange {
25 pub start: u64,
26 pub end: u64,
27}
28
29impl SlotRange {
30 pub fn new(start: u64, end: u64) -> Result<Self, InvertedSlotRange> {
32 if start > end {
33 return Err(InvertedSlotRange { start, end });
34 }
35 Ok(Self { start, end })
36 }
37
38 pub const fn new_unchecked(start: u64, end: u64) -> Self {
41 Self { start, end }
42 }
43
44 pub const fn from_count(start: u64, count: u64) -> Self {
47 Self {
48 start,
49 end: start.saturating_add(count.saturating_sub(1)),
50 }
51 }
52
53 pub const fn start(&self) -> u64 {
54 self.start
55 }
56
57 pub const fn end(&self) -> u64 {
58 self.end
59 }
60
61 pub const fn len(&self) -> u64 {
63 self.end - self.start + 1
64 }
65
66 pub const fn is_empty(&self) -> bool {
69 false
70 }
71
72 pub const fn contains(&self, slot: u64) -> bool {
73 slot >= self.start && slot <= self.end
74 }
75}
76
77impl fmt::Display for SlotRange {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 write!(f, "{}..={}", self.start, self.end)
80 }
81}
82
83impl From<SlotRange> for std::ops::RangeInclusive<u64> {
84 fn from(range: SlotRange) -> Self {
85 range.start..=range.end
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn an_inverted_range_is_rejected() {
95 assert_eq!(
96 SlotRange::new(10, 9),
97 Err(InvertedSlotRange { start: 10, end: 9 })
98 );
99 assert!(SlotRange::new(10, 10).is_ok());
100 }
101
102 #[test]
103 fn length_is_inclusive_of_both_bounds() {
104 assert_eq!(SlotRange::new(10, 10).expect("range").len(), 1);
105 assert_eq!(SlotRange::new(10, 19).expect("range").len(), 10);
106 }
107
108 #[test]
109 fn from_count_matches_the_start_plus_count_convention() {
110 assert_eq!(
111 SlotRange::from_count(100, 1),
112 SlotRange::new_unchecked(100, 100)
113 );
114 assert_eq!(
115 SlotRange::from_count(100, 10),
116 SlotRange::new_unchecked(100, 109)
117 );
118 assert_eq!(
120 SlotRange::from_count(100, 0),
121 SlotRange::new_unchecked(100, 100)
122 );
123 }
124
125 #[test]
126 fn contains_is_inclusive() {
127 let range = SlotRange::new(10, 12).expect("range");
128 assert!(range.contains(10) && range.contains(12));
129 assert!(!range.contains(9) && !range.contains(13));
130 }
131}