1use crate::NonZeroDecimal;
2use rust_decimal::Decimal;
3use std::num::NonZeroUsize;
4
5#[derive(Debug, PartialEq)]
6pub struct TimeRange {
7 pub start: Decimal,
8 end: NonZeroDecimal,
9}
10
11impl TimeRange {
12 pub fn duration(&self) -> NonZeroDecimal {
13 NonZeroDecimal::try_new((*self.end - self.start).round_dp(3)).unwrap()
18 }
19}
20
21#[derive(Debug, PartialEq)]
22pub struct TimeRanges {
23 start: Decimal,
24 max_step_size: NonZeroUsize,
25 end: NonZeroDecimal,
26}
27
28impl TimeRanges {
29 pub fn try_new<A, B, C>(start: A, max_step_size: B, end: C) -> Option<Self>
30 where
31 A: TryInto<Decimal>,
32 B: TryInto<NonZeroUsize>,
33 C: TryInto<Decimal>,
34 {
35 let start = start.try_into().ok()?;
36 let end = NonZeroDecimal::try_new(end.try_into().ok()?)?;
37 if start >= *end {
38 return None;
39 }
40 let max_step_size = max_step_size.try_into().ok()?;
41 Some(Self {
42 start,
43 max_step_size,
44 end,
45 })
46 }
47 pub fn end(&self) -> &Decimal {
48 &self.end
49 }
50}
51
52impl Iterator for TimeRanges {
53 type Item = TimeRange;
54
55 fn next(&mut self) -> Option<Self::Item> {
56 let start = self.start;
57 let end = self.end.get();
58 if start == *end {
59 return None;
60 }
61 let step_size: Decimal = self.max_step_size.get().try_into().ok()?;
62 let next_start = vec![(start + step_size).round(), *end]
63 .into_iter()
64 .min()
65 .unwrap();
66 self.start = next_start;
67 Some(TimeRange {
68 start,
69 end: NonZeroDecimal::try_new(next_start)?,
70 })
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn flatten() {
80 let max_step_size = NonZeroUsize::new(10).unwrap();
82
83 let time_ranges = vec![
84 TimeRanges::try_new(0, max_step_size, "6.675000").unwrap(),
85 TimeRanges::try_new("6.675000", max_step_size, "35").unwrap(),
86 ];
87 let actual = time_ranges
88 .into_iter()
89 .flatten()
90 .collect::<Vec<TimeRange>>();
91 let expected = vec![
92 TimeRange {
93 start: Decimal::ZERO,
94 end: "6.675000".try_into().unwrap(),
95 },
96 TimeRange {
97 start: "6.675000".try_into().unwrap(),
98 end: "17".try_into().unwrap(),
99 },
100 TimeRange {
101 start: "17".try_into().unwrap(),
102 end: "27".try_into().unwrap(),
103 },
104 TimeRange {
105 start: "27".try_into().unwrap(),
106 end: "35".try_into().unwrap(),
107 },
108 ];
109 assert_eq!(actual, expected);
110 }
111 #[test]
112 fn start_equals_end() {
113 let actual = TimeRanges::try_new(1, 2, 1);
114 assert_eq!(actual, None);
115 }
116
117 #[test]
118 fn start_gt_end() {
119 let actual = TimeRanges::try_new(2, 2, 1);
120 assert_eq!(actual, None);
121 }
122 #[test]
123 fn next_doesnt_exceed_end() {
124 let start = 0;
125
126 let max_step_size = 10;
128
129 let end = Decimal::from_str_exact("9.76").unwrap();
130
131 let mut time_ranges = TimeRanges::try_new(start, max_step_size, end)
132 .unwrap()
133 .into_iter();
134 assert_eq!(
135 time_ranges.next().unwrap(),
136 TimeRange {
137 start: Decimal::ZERO,
138 end: NonZeroDecimal::try_new(end).unwrap()
139 }
140 );
141 assert!(time_ranges.next().is_none());
142 }
143
144 #[test]
145 fn start_is_zero_multiple_next() {
146 let start = 0;
147 let max_step_size = 3;
148 let end = Decimal::from_str_exact("9.76").unwrap();
149 let mut time_ranges = TimeRanges::try_new(start, max_step_size, end)
150 .unwrap()
151 .into_iter();
152 assert_eq!(
153 time_ranges.next().unwrap(),
154 TimeRange {
155 start: Decimal::ZERO,
156 end: NonZeroDecimal::try_new(max_step_size).unwrap()
157 }
158 );
159 assert_eq!(
160 time_ranges.next().unwrap(),
161 TimeRange {
162 start: max_step_size.try_into().unwrap(),
163 end: NonZeroDecimal::try_new(max_step_size * 2).unwrap()
164 }
165 );
166 assert_eq!(
167 time_ranges.next().unwrap(),
168 TimeRange {
169 start: (max_step_size * 2).try_into().unwrap(),
170 end: NonZeroDecimal::try_new(max_step_size * 3).unwrap()
171 }
172 );
173 assert_eq!(
174 time_ranges.next().unwrap(),
175 TimeRange {
176 start: (max_step_size * 3).try_into().unwrap(),
177 end: NonZeroDecimal::try_new(end).unwrap()
178 }
179 );
180 assert!(time_ranges.next().is_none());
181 }
182
183 #[test]
184 fn start_is_decimal_multiple_next() {
185 let start = Decimal::from_str_exact("522.981000").unwrap();
186 let end = Decimal::from_str_exact("568.151000").unwrap();
187 let max_step_size = 20;
188
189 let mut time_ranges = TimeRanges::try_new(start, max_step_size, end)
190 .unwrap()
191 .into_iter();
192 assert_eq!(
193 time_ranges.next().unwrap(),
194 TimeRange {
195 start,
196 end: NonZeroDecimal::try_new(543).unwrap()
197 }
198 );
199 assert_eq!(
200 time_ranges.next().unwrap(),
201 TimeRange {
202 start: Decimal::from_str_exact("543").unwrap(),
203 end: NonZeroDecimal::try_new(563).unwrap()
204 }
205 );
206 assert_eq!(
207 time_ranges.next().unwrap(),
208 TimeRange {
209 start: Decimal::from_str_exact("563").unwrap(),
210 end: NonZeroDecimal::try_new(end).unwrap()
211 }
212 );
213 }
214}