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