1use crate::api_traits::Timestamp;
4use crate::remote::{ListBodyArgs, ListSortMode};
5use crate::Error;
6
7use crate::error::{self, GRError};
8use crate::Result;
9use chrono::{DateTime, Local};
10use std;
11use std::fmt::{Display, Formatter};
12use std::ops::{Add, AddAssign, Deref, Div, Sub};
13use std::str::FromStr;
14use std::time::Duration;
15
16enum Time {
17 Second,
18 Minute,
19 Hour,
20 Day,
21}
22
23impl Time {
24 fn to_seconds(&self) -> u64 {
25 match self {
26 Time::Second => 1,
27 Time::Minute => 60,
28 Time::Hour => 3600,
29 Time::Day => 86400,
30 }
31 }
32}
33
34impl TryFrom<char> for Time {
35 type Error = Error;
36
37 fn try_from(time: char) -> std::result::Result<Self, Self::Error> {
38 match time {
39 's' => Ok(Time::Second),
40 'm' => Ok(Time::Minute),
41 'h' => Ok(Time::Hour),
42 'd' => Ok(Time::Day),
43 _ => Err(error::gen(format!(
44 "Unknown char time format: {time} - valid types are s, m, h, d"
45 ))),
46 }
47 }
48}
49
50pub fn now_epoch_seconds() -> Seconds {
51 let now_epoch = std::time::SystemTime::now()
52 .duration_since(std::time::UNIX_EPOCH)
53 .unwrap()
54 .as_secs();
55 Seconds(now_epoch)
56}
57
58pub fn epoch_to_minutes_relative(epoch_seconds: Seconds) -> String {
59 let now = now_epoch_seconds();
60 let diff = now - epoch_seconds;
61 let minutes = diff / Seconds::new(60);
62 minutes.to_string()
63}
64
65pub fn epoch_to_seconds_relative(epoch_seconds: Seconds) -> String {
66 let now = now_epoch_seconds();
67 let diff = now - epoch_seconds;
68 diff.to_string()
69}
70
71#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
72pub struct Seconds(u64);
73
74impl Seconds {
75 pub fn new(seconds: u64) -> Self {
76 Seconds(seconds)
77 }
78}
79
80impl FromStr for Seconds {
81 type Err = GRError;
82
83 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
84 match s.parse::<u64>() {
85 Ok(seconds) => Ok(Seconds(seconds)),
86 Err(err) => Err(GRError::TimeConversionError(format!(
87 "Could not convert {s} to time format: {err}",
88 ))),
89 }
90 }
91}
92
93impl Sub<Seconds> for Seconds {
94 type Output = Seconds;
95
96 fn sub(self, rhs: Seconds) -> Self::Output {
97 if self.0 < rhs.0 {
98 return Seconds(rhs.0 - self.0);
99 }
100 Seconds(self.0 - rhs.0)
101 }
102}
103
104impl Add<Seconds> for Seconds {
105 type Output = Seconds;
106
107 fn add(self, rhs: Seconds) -> Self::Output {
108 Seconds(self.0 + rhs.0)
109 }
110}
111
112impl Div<Seconds> for Seconds {
113 type Output = Seconds;
114
115 fn div(self, rhs: Seconds) -> Self::Output {
116 Seconds(self.0 / rhs.0)
117 }
118}
119
120impl Deref for Seconds {
121 type Target = u64;
122
123 fn deref(&self) -> &Self::Target {
124 &self.0
125 }
126}
127
128impl From<u64> for Seconds {
129 fn from(seconds: u64) -> Self {
130 Seconds(seconds)
131 }
132}
133
134impl Display for Seconds {
135 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
136 write!(f, "{}", self.0)
137 }
138}
139
140#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
141pub struct Milliseconds(u64);
142
143impl Milliseconds {
144 pub fn new(milliseconds: u64) -> Self {
145 Milliseconds(milliseconds)
146 }
147}
148
149impl Deref for Milliseconds {
150 type Target = u64;
151
152 fn deref(&self) -> &Self::Target {
153 &self.0
154 }
155}
156
157impl From<u64> for Milliseconds {
158 fn from(milliseconds: u64) -> Self {
159 Milliseconds(milliseconds)
160 }
161}
162
163impl From<Milliseconds> for Duration {
164 fn from(milliseconds: Milliseconds) -> Self {
165 Duration::from_millis(milliseconds.0)
166 }
167}
168
169impl From<Seconds> for Milliseconds {
170 fn from(seconds: Seconds) -> Self {
171 Milliseconds(seconds.0 * 1000)
172 }
173}
174
175impl Add<Milliseconds> for Milliseconds {
176 type Output = Milliseconds;
177
178 fn add(self, rhs: Milliseconds) -> Self::Output {
179 Milliseconds(self.0 + rhs.0)
180 }
181}
182
183impl AddAssign<Milliseconds> for Milliseconds {
184 fn add_assign(&mut self, rhs: Milliseconds) {
185 self.0 += rhs.0;
186 }
187}
188
189impl Display for Milliseconds {
190 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
191 write!(f, "{}", self.0)
192 }
193}
194
195fn string_to_seconds(str_fmt: &str) -> Result<Seconds> {
201 let mut seconds: u64 = 0;
202 for c in str_fmt.chars() {
203 if c.is_ascii_digit() {
204 seconds = seconds * 10 + c.to_digit(10).unwrap() as u64;
205 } else {
206 if c.is_whitespace() {
207 continue;
208 }
209 seconds *= Time::try_from(c)?.to_seconds();
210 break;
211 }
212 }
213 Ok(Seconds(seconds))
214}
215
216impl TryFrom<&str> for Seconds {
217 type Error = GRError;
218
219 fn try_from(str_fmt: &str) -> std::result::Result<Self, Self::Error> {
220 match string_to_seconds(str_fmt) {
221 Ok(seconds) => Ok(seconds),
222 Err(err) => Err(GRError::TimeConversionError(format!(
223 "Could not convert {str_fmt} to time format: {err}",
224 ))),
225 }
226 }
227}
228
229pub fn sort_filter_by_date<T: Timestamp>(
230 data: Vec<T>,
231 list_args: Option<ListBodyArgs>,
232) -> Result<Vec<T>> {
233 if let Some(list_args) = list_args {
234 let (created_after, created_before) = (list_args.created_after, list_args.created_before);
235 match (created_after, created_before) {
236 (Some(created_after), Some(created_before)) => {
237 let created_after = created_after.parse::<DateTime<Local>>().map_err(|err| {
238 GRError::TimeConversionError(format!(
239 "Could not convert {created_after} to date format: {err}",
240 ))
241 })?;
242 let created_before = created_before.parse::<DateTime<Local>>().map_err(|err| {
243 GRError::TimeConversionError(format!(
244 "Could not convert {created_before} to date format: {err}",
245 ))
246 })?;
247 return Ok(sort_by_date(
248 data,
249 Some(created_after),
250 Some(created_before),
251 Some(list_args.sort_mode),
252 ));
253 }
254 (Some(created_after), None) => {
255 let created_after = created_after.parse::<DateTime<Local>>().map_err(|err| {
256 GRError::TimeConversionError(format!(
257 "Could not convert {created_after} to date format: {err}",
258 ))
259 })?;
260 return Ok(sort_by_date(
261 data,
262 Some(created_after),
263 None,
264 Some(list_args.sort_mode),
265 ));
266 }
267 (None, Some(created_before)) => {
268 let created_before = created_before.parse::<DateTime<Local>>().map_err(|err| {
269 GRError::TimeConversionError(format!(
270 "Could not convert {created_before} to date format: {err}",
271 ))
272 })?;
273 return Ok(sort_by_date(
274 data,
275 None,
276 Some(created_before),
277 Some(list_args.sort_mode),
278 ));
279 }
280 (None, None) => {
281 return Ok(sort_by_date(data, None, None, Some(list_args.sort_mode)));
282 }
283 }
284 }
285 Ok(sort_by_date(data, None, None, Some(ListSortMode::Asc)))
286}
287
288fn sort_by_date<T: Timestamp>(
289 data: Vec<T>,
290 created_after: Option<DateTime<Local>>,
291 created_before: Option<DateTime<Local>>,
292 sort_mode: Option<ListSortMode>,
293) -> Vec<T> {
294 let mut data_dates = match (created_after, created_before) {
295 (Some(created_after), Some(created_before)) => data
296 .into_iter()
297 .filter_map(|item| {
298 let item_date = item.created_at().parse::<DateTime<Local>>().ok()?;
299 if item_date >= created_after && item_date <= created_before {
300 return Some((item, item_date));
301 }
302 None
303 })
304 .collect::<Vec<(T, DateTime<Local>)>>(),
305 (Some(created_after), None) => data
306 .into_iter()
307 .filter_map(|item| {
308 let item_date = item.created_at().parse::<DateTime<Local>>().ok()?;
309 if item_date >= created_after {
310 return Some((item, item_date));
311 }
312 None
313 })
314 .collect::<Vec<(T, DateTime<Local>)>>(),
315 (None, Some(created_before)) => data
316 .into_iter()
317 .filter_map(|item| {
318 let item_date = item.created_at().parse::<DateTime<Local>>().ok()?;
319 if item_date <= created_before {
320 return Some((item, item_date));
321 }
322 None
323 })
324 .collect::<Vec<(T, DateTime<Local>)>>(),
325 (None, None) => data
326 .into_iter()
327 .map(|item| {
328 let item_date = item.created_at().parse::<DateTime<Local>>().unwrap();
329 (item, item_date)
330 })
331 .collect::<Vec<(T, DateTime<Local>)>>(),
332 };
333 if let Some(sort_mode) = sort_mode {
334 match sort_mode {
335 ListSortMode::Asc => data_dates.sort_by(|a, b| a.1.cmp(&b.1)),
336 ListSortMode::Desc => data_dates.sort_by(|a, b| b.1.cmp(&a.1)),
337 }
338 }
339 data_dates.into_iter().map(|(item, _)| item).collect()
340}
341
342pub fn compute_duration(start: &str, end: &str) -> u64 {
343 let created_at = chrono::DateTime::parse_from_rfc3339(start).unwrap();
344 let updated_at = chrono::DateTime::parse_from_rfc3339(end).unwrap();
345 updated_at.signed_duration_since(created_at).num_seconds() as u64
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351
352 #[test]
353 fn test_time_formatted_string_to_seconds() {
354 let test_table = vec![
355 ("1s", Seconds(1)),
356 ("2s", Seconds(2)),
357 ("2 seconds", Seconds(2)),
358 ("2 second", Seconds(2)),
359 ("2seconds", Seconds(2)),
360 ("2second", Seconds(2)),
361 ("2 s", Seconds(2)),
362 ("1m", Seconds(60)),
363 ("2m", Seconds(120)),
364 ("2 minutes", Seconds(120)),
365 ("2 minute", Seconds(120)),
366 ("2minutes", Seconds(120)),
367 ("2minute", Seconds(120)),
368 ("2 m", Seconds(120)),
369 ("1h", Seconds(3600)),
370 ("2h", Seconds(7200)),
371 ("2 hours", Seconds(7200)),
372 ("2 hour", Seconds(7200)),
373 ("2hours", Seconds(7200)),
374 ("2hour", Seconds(7200)),
375 ("2 h", Seconds(7200)),
376 ("1d", Seconds(86400)),
377 ("2d", Seconds(172800)),
378 ("2 days", Seconds(172800)),
379 ("2 day", Seconds(172800)),
380 ("2days", Seconds(172800)),
381 ("2day", Seconds(172800)),
382 ("2 d", Seconds(172800)),
383 ("300", Seconds(300)),
385 ("", Seconds(0)),
387 ];
388 for (input, expected) in test_table {
389 let actual = string_to_seconds(input).unwrap();
390 assert_eq!(expected.0, actual.0);
391 }
392 }
393
394 #[test]
395 fn test_cannot_convert_time_formatted_string_to_seconds() {
396 let input_err = "2x"; assert!(string_to_seconds(input_err).is_err());
398 }
399
400 struct TimestampMock {
401 created_at: String,
402 }
403
404 impl TimestampMock {
405 fn new(created_at: &str) -> Self {
406 TimestampMock {
407 created_at: created_at.to_string(),
408 }
409 }
410 }
411
412 impl Timestamp for TimestampMock {
413 fn created_at(&self) -> String {
414 self.created_at.clone()
415 }
416 }
417
418 #[test]
419 fn test_filter_date_created_after_iso_8601() {
420 let created_after = "2021-01-01T00:00:00Z".to_string();
421 let list_args = ListBodyArgs::builder()
422 .created_after(Some(created_after))
423 .build()
424 .unwrap();
425 let data = vec![
426 TimestampMock::new("2021-01-01T00:00:00Z"),
427 TimestampMock::new("2020-12-31T00:00:00Z"),
428 TimestampMock::new("2021-03-02T00:00:00Z"),
429 TimestampMock::new("2021-02-02T00:00:00Z"),
430 ];
431 let filtered = sort_filter_by_date(data, Some(list_args)).unwrap();
432 assert_eq!(3, filtered.len());
433 assert_eq!("2021-01-01T00:00:00Z", filtered[0].created_at());
434 assert_eq!("2021-02-02T00:00:00Z", filtered[1].created_at());
435 assert_eq!("2021-03-02T00:00:00Z", filtered[2].created_at());
436 }
437
438 #[test]
439 fn test_filter_date_created_after_iso_8601_no_date() {
440 let data = vec![
441 TimestampMock::new("2021-01-01T00:00:00Z"),
442 TimestampMock::new("2020-12-31T00:00:00Z"),
443 TimestampMock::new("2021-01-02T00:00:00Z"),
444 ];
445 let sorted = sort_filter_by_date(data, None).unwrap();
447 assert_eq!(3, sorted.len());
448 assert_eq!("2020-12-31T00:00:00Z", sorted[0].created_at());
449 assert_eq!("2021-01-01T00:00:00Z", sorted[1].created_at());
450 assert_eq!("2021-01-02T00:00:00Z", sorted[2].created_at());
451 }
452
453 #[test]
454 fn test_filter_date_created_at_iso_8601_invalid_date_filtered_out() {
455 let created_after = "2021-01-01T00:00:00Z".to_string();
456 let list_args = ListBodyArgs::builder()
457 .created_after(Some(created_after))
458 .build()
459 .unwrap();
460 let data = vec![
461 TimestampMock::new("2021-01/01"),
462 TimestampMock::new("2020-12-31T00:00:00Z"),
463 TimestampMock::new("2021-01-02T00:00:00Z"),
464 ];
465 let filtered = sort_filter_by_date(data, Some(list_args)).unwrap();
466 assert_eq!(1, filtered.len());
467 }
468
469 #[test]
470 fn test_created_after_invalid_date_is_error() {
471 let created_after = "2021-01/01".to_string();
472 let list_args = ListBodyArgs::builder()
473 .created_after(Some(created_after))
474 .build()
475 .unwrap();
476 let data = vec![
477 TimestampMock::new("2021-01/01"),
478 TimestampMock::new("2020-12-31T00:00:00Z"),
479 TimestampMock::new("2021-01-02T00:00:00Z"),
480 ];
481 let result = sort_filter_by_date(data, Some(list_args));
482 match result {
483 Err(err) => match err.downcast_ref::<GRError>() {
484 Some(GRError::TimeConversionError(_)) => (),
485 _ => panic!("Expected TimeConversionError"),
486 },
487 _ => panic!("Expected TimeConversionError"),
488 }
489 }
490
491 #[test]
492 fn test_sort_by_date_descending_order() {
493 let data = vec![
494 TimestampMock::new("2021-01-01T00:00:00Z"),
495 TimestampMock::new("2020-12-31T00:00:00Z"),
496 TimestampMock::new("2021-01-02T00:00:00Z"),
497 ];
498 let sorted = sort_by_date(data, None, None, Some(ListSortMode::Desc));
499 assert_eq!(3, sorted.len());
500 assert_eq!("2021-01-02T00:00:00Z", sorted[0].created_at());
501 assert_eq!("2021-01-01T00:00:00Z", sorted[1].created_at());
502 assert_eq!("2020-12-31T00:00:00Z", sorted[2].created_at());
503 }
504
505 #[test]
506 fn test_filter_by_created_before_date() {
507 let created_before = "2021-01-01T00:00:00Z".to_string();
508 let list_args = ListBodyArgs::builder()
509 .created_before(Some(created_before))
510 .build()
511 .unwrap();
512 let data = vec![
513 TimestampMock::new("2021-01-01T00:00:00Z"),
514 TimestampMock::new("2020-12-31T00:00:00Z"),
515 TimestampMock::new("2021-03-02T00:00:00Z"),
516 TimestampMock::new("2021-02-02T00:00:00Z"),
517 ];
518 let filtered = sort_filter_by_date(data, Some(list_args)).unwrap();
519 assert_eq!(2, filtered.len());
520 assert_eq!("2020-12-31T00:00:00Z", filtered[0].created_at());
521 assert_eq!("2021-01-01T00:00:00Z", filtered[1].created_at());
522 }
523
524 #[test]
525 fn test_filter_by_created_after_and_created_before_date() {
526 let created_after = "2021-01-01T00:00:00Z".to_string();
527 let created_before = "2021-02-01T00:00:00Z".to_string();
528 let list_args = ListBodyArgs::builder()
529 .created_after(Some(created_after))
530 .created_before(Some(created_before))
531 .build()
532 .unwrap();
533 let data = vec![
534 TimestampMock::new("2021-01-01T00:00:00Z"),
535 TimestampMock::new("2021-01-20T00:00:00Z"),
536 TimestampMock::new("2020-12-31T00:00:00Z"),
537 TimestampMock::new("2021-03-02T00:00:00Z"),
538 TimestampMock::new("2021-02-02T00:00:00Z"),
539 ];
540 let filtered = sort_filter_by_date(data, Some(list_args)).unwrap();
541 assert_eq!(2, filtered.len());
542 assert_eq!("2021-01-01T00:00:00Z", filtered[0].created_at());
543 assert_eq!("2021-01-20T00:00:00Z", filtered[1].created_at());
544 }
545
546 #[test]
547 fn test_no_filter_with_no_created_after_and_no_created_before() {
548 let data = vec![
549 TimestampMock::new("2021-01-01T00:00:00Z"),
550 TimestampMock::new("2020-12-31T00:00:00Z"),
551 TimestampMock::new("2021-03-02T00:00:00Z"),
552 TimestampMock::new("2021-02-02T00:00:00Z"),
553 ];
554 let filtered = sort_filter_by_date(data, None).unwrap();
555 assert_eq!(4, filtered.len());
556 assert_eq!("2020-12-31T00:00:00Z", filtered[0].created_at());
557 assert_eq!("2021-01-01T00:00:00Z", filtered[1].created_at());
558 assert_eq!("2021-02-02T00:00:00Z", filtered[2].created_at());
559 assert_eq!("2021-03-02T00:00:00Z", filtered[3].created_at());
560 }
561
562 #[test]
563 fn test_error_if_created_before_invalid_non_iso_8601_date() {
564 let created_before = "2021-01/01".to_string();
565 let list_args = ListBodyArgs::builder()
566 .created_before(Some(created_before))
567 .build()
568 .unwrap();
569 let data = vec![
570 TimestampMock::new("2020-12-31T00:00:00Z"),
571 TimestampMock::new("2021-01-02T00:00:00Z"),
572 ];
573 let result = sort_filter_by_date(data, Some(list_args));
574 match result {
575 Err(err) => match err.downcast_ref::<GRError>() {
576 Some(GRError::TimeConversionError(_)) => (),
577 _ => panic!("Expected TimeConversionError"),
578 },
579 _ => panic!("Expected TimeConversionError"),
580 }
581 }
582
583 #[test]
584 fn test_created_after_and_before_available_after_is_invalid_date() {
585 let created_after = "2021-01/01".to_string();
586 let created_before = "2021-01-01T00:00:00Z".to_string();
587 let list_args = ListBodyArgs::builder()
588 .created_after(Some(created_after))
589 .created_before(Some(created_before))
590 .build()
591 .unwrap();
592 let data = vec![
593 TimestampMock::new("2020-12-31T00:00:00Z"),
594 TimestampMock::new("2021-01-02T00:00:00Z"),
595 ];
596 let result = sort_filter_by_date(data, Some(list_args));
597 match result {
598 Err(err) => match err.downcast_ref::<GRError>() {
599 Some(GRError::TimeConversionError(_)) => (),
600 _ => panic!("Expected TimeConversionError"),
601 },
602 _ => panic!("Expected TimeConversionError"),
603 }
604 }
605
606 #[test]
607 fn test_created_after_and_before_available_before_is_invalid_date() {
608 let created_after = "2021-01-01T00:00:00Z".to_string();
609 let created_before = "2021-01/01".to_string();
610 let list_args = ListBodyArgs::builder()
611 .created_after(Some(created_after))
612 .created_before(Some(created_before))
613 .build()
614 .unwrap();
615 let data = vec![
616 TimestampMock::new("2020-12-31T00:00:00Z"),
617 TimestampMock::new("2021-01-02T00:00:00Z"),
618 ];
619 let result = sort_filter_by_date(data, Some(list_args));
620 match result {
621 Err(err) => match err.downcast_ref::<GRError>() {
622 Some(GRError::TimeConversionError(_)) => (),
623 _ => panic!("Expected TimeConversionError"),
624 },
625 _ => panic!("Expected TimeConversionError"),
626 }
627 }
628
629 #[test]
630 fn test_compute_duration() {
631 let created_at = "2020-01-01T00:00:00Z";
632 let updated_at = "2020-01-01T00:01:00Z";
633 let duration = compute_duration(created_at, updated_at);
634 assert_eq!(60, duration);
635 }
636}