1#![cfg_attr(feature = "serde", doc = "```rust")]
63#![cfg_attr(not(feature = "serde"), doc = "```ignore")]
64#[cfg(feature = "serde")]
80use serde::de::Unexpected;
81use std::borrow::{Borrow, BorrowMut};
82use std::convert::TryFrom;
83#[cfg(feature = "serde")]
84use std::fmt;
85use std::iter::Sum;
86#[cfg(feature = "serde")]
87use std::marker::PhantomData;
88use std::num::ParseIntError;
89use std::ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
90use std::str::FromStr;
91use std::time::Duration;
92
93const YEAR_IN_NANO: u128 = 31_556_926_000_000_000;
94const WEEK_IN_NANO: u128 = 604_800_000_000_000;
95const DAY_IN_NANO: u128 = 86_400_000_000_000;
96const HOUR_IN_NANO: u128 = 3_600_000_000_000;
97const MINUTE_IN_NANO: u128 = 60_000_000_000;
98const SECOND_IN_NANO: u128 = 1_000_000_000;
99const MILLISECOND_IN_NANO: u128 = 1_000_000;
100const MICROSECOND_IN_NANO: u128 = 1000;
101
102const HOUR_IN_SECONDS: u32 = 3600;
103const MINUTE_IN_SECONDS: u32 = 60;
104const DAY_IN_SECONDS: u32 = 86_400;
105const WEEK_IN_SECONDS: u32 = 604_800;
106const YEAR_IN_SECONDS: u32 = 31_556_926;
107
108pub type Result<T> = std::result::Result<T, Error>;
109
110#[derive(Clone, Debug, Eq, PartialEq)]
111pub enum Error {
112 Format,
113 Overflow,
114 ParseInt(ParseIntError),
115}
116
117impl std::fmt::Display for Error {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 match self {
120 Self::Format => write!(
121 f,
122 "missing time duration format, must be multiples of `[0-9]+(ns|us|ms|[smhdwy])`"
123 ),
124 Self::Overflow => write!(f, "number is too large to fit in target type"),
125 Self::ParseInt(err) => write!(f, "{err}"),
126 }
127 }
128}
129
130impl std::error::Error for Error {
131 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
132 match self {
133 Self::Format | Self::Overflow => None,
134 Self::ParseInt(err) => Some(err),
135 }
136 }
137}
138
139impl From<ParseIntError> for Error {
140 fn from(value: ParseIntError) -> Self {
141 Self::ParseInt(value)
142 }
143}
144
145#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
146pub struct DurationString(Duration);
147
148impl DurationString {
149 #[must_use]
150 pub const fn new(duration: Duration) -> DurationString {
151 DurationString(duration)
152 }
153
154 #[allow(clippy::missing_errors_doc)]
155 pub fn from_string(duration: String) -> Result<Self> {
156 DurationString::try_from(duration)
157 }
158}
159
160impl std::fmt::Display for DurationString {
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 let s: String = (*self).into();
163 write!(f, "{s}")
164 }
165}
166
167impl From<DurationString> for Duration {
168 fn from(value: DurationString) -> Self {
169 value.0
170 }
171}
172
173impl From<DurationString> for String {
174 fn from(value: DurationString) -> Self {
175 let ns = value.0.as_nanos();
176 if ns % YEAR_IN_NANO == 0 {
177 return (ns / YEAR_IN_NANO).to_string() + "y";
178 }
179 if ns % WEEK_IN_NANO == 0 {
180 return (ns / WEEK_IN_NANO).to_string() + "w";
181 }
182 if ns % DAY_IN_NANO == 0 {
183 return (ns / DAY_IN_NANO).to_string() + "d";
184 }
185 if ns % HOUR_IN_NANO == 0 {
186 return (ns / HOUR_IN_NANO).to_string() + "h";
187 }
188 if ns % MINUTE_IN_NANO == 0 {
189 return (ns / MINUTE_IN_NANO).to_string() + "m";
190 }
191 if ns % SECOND_IN_NANO == 0 {
192 return (ns / SECOND_IN_NANO).to_string() + "s";
193 }
194 if ns % MILLISECOND_IN_NANO == 0 {
195 return (ns / MILLISECOND_IN_NANO).to_string() + "ms";
196 }
197 if ns % MICROSECOND_IN_NANO == 0 {
198 return (ns / MICROSECOND_IN_NANO).to_string() + "us";
199 }
200 ns.to_string() + "ns"
201 }
202}
203
204impl From<Duration> for DurationString {
205 fn from(duration: Duration) -> Self {
206 DurationString(duration)
207 }
208}
209
210impl TryFrom<String> for DurationString {
211 type Error = Error;
212
213 fn try_from(duration: String) -> std::result::Result<Self, Self::Error> {
214 duration.parse()
215 }
216}
217
218impl FromStr for DurationString {
219 type Err = Error;
220
221 fn from_str(duration: &str) -> std::result::Result<Self, Self::Err> {
222 let duration: Vec<char> = duration.chars().filter(|c| !c.is_whitespace()).collect();
223 let mut grouped_durations: Vec<(Vec<char>, Vec<char>)> = vec![(vec![], vec![])];
224 for i in 0..duration.len() {
225 if duration[i].is_numeric() {
227 grouped_durations.last_mut().unwrap().0.push(duration[i]);
228 } else {
229 grouped_durations.last_mut().unwrap().1.push(duration[i]);
230 }
231 if i != duration.len() - 1 && !duration[i].is_numeric() && duration[i + 1].is_numeric()
232 {
233 grouped_durations.push((vec![], vec![]));
235 }
236 }
237 if grouped_durations.is_empty() {
238 return Err(Error::Format);
240 }
241 let mut total_duration = Duration::new(0, 0);
242 for (period, format) in grouped_durations {
243 let period = match period.iter().collect::<String>().parse::<u64>() {
244 Ok(period) => Ok(period),
245 Err(err) => Err(Error::ParseInt(err)),
246 }?;
247 let multiply_period = |multiplier: u32| -> std::result::Result<Duration, Self::Err> {
248 Duration::from_secs(period)
249 .checked_mul(multiplier)
250 .ok_or(Error::Overflow)
251 };
252 let period_duration = match format.iter().collect::<String>().as_ref() {
253 "ns" => Ok(Duration::from_nanos(period)),
254 "us" => Ok(Duration::from_micros(period)),
255 "ms" => Ok(Duration::from_millis(period)),
256 "s" => Ok(Duration::from_secs(period)),
257 "m" => multiply_period(MINUTE_IN_SECONDS),
258 "h" => multiply_period(HOUR_IN_SECONDS),
259 "d" => multiply_period(DAY_IN_SECONDS),
260 "w" => multiply_period(WEEK_IN_SECONDS),
261 "y" => multiply_period(YEAR_IN_SECONDS),
262 _ => Err(Error::Format),
263 }?;
264 total_duration = total_duration
265 .checked_add(period_duration)
266 .ok_or(Error::Overflow)?;
267 }
268 Ok(DurationString(total_duration))
269 }
270}
271
272impl Deref for DurationString {
273 type Target = Duration;
274
275 fn deref(&self) -> &Self::Target {
276 &self.0
277 }
278}
279
280impl DerefMut for DurationString {
281 fn deref_mut(&mut self) -> &mut Self::Target {
282 &mut self.0
283 }
284}
285
286impl Borrow<Duration> for DurationString {
287 fn borrow(&self) -> &Duration {
288 &self.0
289 }
290}
291
292impl BorrowMut<Duration> for DurationString {
293 fn borrow_mut(&mut self) -> &mut Duration {
294 &mut self.0
295 }
296}
297
298impl PartialEq<Duration> for DurationString {
299 fn eq(&self, other: &Duration) -> bool {
300 self.0.eq(other)
301 }
302}
303
304impl PartialEq<DurationString> for Duration {
305 fn eq(&self, other: &DurationString) -> bool {
306 self.eq(&other.0)
307 }
308}
309
310impl PartialOrd<Duration> for DurationString {
311 fn partial_cmp(&self, other: &Duration) -> Option<std::cmp::Ordering> {
312 self.0.partial_cmp(other)
313 }
314}
315
316impl PartialOrd<DurationString> for Duration {
317 fn partial_cmp(&self, other: &DurationString) -> Option<std::cmp::Ordering> {
318 self.partial_cmp(&other.0)
319 }
320}
321
322impl Add for DurationString {
323 type Output = Self;
324
325 fn add(self, other: Self) -> Self::Output {
326 Self::new(self.0.add(other.0))
327 }
328}
329
330impl Add<Duration> for DurationString {
331 type Output = Self;
332
333 fn add(self, other: Duration) -> Self::Output {
334 Self::new(self.0.add(other))
335 }
336}
337
338impl Add<DurationString> for Duration {
339 type Output = Self;
340
341 fn add(self, other: DurationString) -> Self::Output {
342 self.add(other.0)
343 }
344}
345
346impl AddAssign for DurationString {
347 fn add_assign(&mut self, other: Self) {
348 self.0.add_assign(other.0);
349 }
350}
351
352impl AddAssign<Duration> for DurationString {
353 fn add_assign(&mut self, other: Duration) {
354 self.0.add_assign(other);
355 }
356}
357
358impl AddAssign<DurationString> for Duration {
359 fn add_assign(&mut self, other: DurationString) {
360 self.add_assign(other.0);
361 }
362}
363
364impl Sub for DurationString {
365 type Output = Self;
366
367 fn sub(self, other: Self) -> Self::Output {
368 Self::new(self.0.sub(other.0))
369 }
370}
371
372impl Sub<Duration> for DurationString {
373 type Output = Self;
374
375 fn sub(self, other: Duration) -> Self::Output {
376 Self::new(self.0.sub(other))
377 }
378}
379
380impl Sub<DurationString> for Duration {
381 type Output = Self;
382
383 fn sub(self, other: DurationString) -> Self::Output {
384 self.sub(other.0)
385 }
386}
387
388impl SubAssign for DurationString {
389 fn sub_assign(&mut self, other: Self) {
390 self.0.sub_assign(other.0);
391 }
392}
393
394impl SubAssign<Duration> for DurationString {
395 fn sub_assign(&mut self, other: Duration) {
396 self.0.sub_assign(other);
397 }
398}
399
400impl SubAssign<DurationString> for Duration {
401 fn sub_assign(&mut self, other: DurationString) {
402 self.sub_assign(other.0);
403 }
404}
405
406impl Mul<u32> for DurationString {
407 type Output = Self;
408
409 fn mul(self, other: u32) -> Self::Output {
410 Self::new(self.0.mul(other))
411 }
412}
413
414impl Mul<DurationString> for u32 {
415 type Output = DurationString;
416
417 fn mul(self, other: DurationString) -> Self::Output {
418 DurationString::new(self.mul(other.0))
419 }
420}
421
422impl MulAssign<u32> for DurationString {
423 fn mul_assign(&mut self, other: u32) {
424 self.0.mul_assign(other);
425 }
426}
427
428impl Div<u32> for DurationString {
429 type Output = Self;
430
431 fn div(self, other: u32) -> Self::Output {
432 Self::new(self.0.div(other))
433 }
434}
435
436impl DivAssign<u32> for DurationString {
437 fn div_assign(&mut self, other: u32) {
438 self.0.div_assign(other);
439 }
440}
441
442impl Sum for DurationString {
443 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
444 Self::new(Duration::sum(iter.map(|duration_string| duration_string.0)))
445 }
446}
447
448impl<'a> Sum<&'a DurationString> for DurationString {
449 fn sum<I: Iterator<Item = &'a DurationString>>(iter: I) -> Self {
450 Self::new(Duration::sum(
451 iter.map(|duration_string| &duration_string.0),
452 ))
453 }
454}
455
456#[cfg(feature = "serde")]
457struct DurationStringVisitor {
458 marker: PhantomData<fn() -> DurationString>,
459}
460
461#[cfg(feature = "serde")]
462impl DurationStringVisitor {
463 fn new() -> Self {
464 Self {
465 marker: PhantomData,
466 }
467 }
468}
469
470#[cfg(feature = "serde")]
471#[allow(clippy::needless_lifetimes)]
472impl<'de> serde::de::Visitor<'de> for DurationStringVisitor {
473 type Value = DurationString;
474
475 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
476 formatter.write_str("string")
477 }
478
479 fn visit_str<E>(self, string: &str) -> std::result::Result<Self::Value, E>
480 where
481 E: serde::de::Error,
482 {
483 match DurationString::from_string(string.to_string()) {
484 Ok(d) => Ok(d),
485 Err(s) => Err(serde::de::Error::invalid_value(
486 Unexpected::Str(&s.to_string()),
487 &self,
488 )),
489 }
490 }
491}
492
493#[cfg(feature = "serde")]
494impl<'de> serde::Deserialize<'de> for DurationString {
495 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
496 where
497 D: serde::Deserializer<'de>,
498 {
499 deserializer.deserialize_str(DurationStringVisitor::new())
500 }
501}
502
503#[cfg(feature = "serde")]
504impl serde::Serialize for DurationString {
505 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
506 where
507 S: serde::Serializer,
508 {
509 serializer.serialize_str(&self.to_string())
510 }
511}
512#[cfg(test)]
513mod tests {
514 use super::*;
515 #[cfg(feature = "serde")]
516 use serde::{Deserialize, Serialize};
517
518 #[cfg(feature = "serde")]
519 #[derive(Serialize, Deserialize)]
520 struct SerdeSupport {
521 d: DurationString,
522 }
523
524 #[cfg(feature = "serde")]
525 #[test]
526 fn test_serialize_trait() {
527 let s = SerdeSupport {
528 d: DurationString::from_string(String::from("1m")).unwrap(),
529 };
530 assert_eq!(r#"{"d":"1m"}"#, serde_json::to_string(&s).unwrap());
531 }
532
533 #[cfg(feature = "serde")]
534 #[test]
535 fn test_deserialize_trait() {
536 let s = r#"{"d":"2m"}"#;
537 match serde_json::from_str::<SerdeSupport>(s) {
538 Ok(v) => {
539 assert_eq!(v.d.to_string(), "2m");
540 }
541 Err(err) => panic!("failed to deserialize: {}", err),
542 }
543 }
544
545 #[test]
546 fn test_string_int_overflow() {
547 DurationString::from_string(String::from("ms")).expect_err("parsing \"ms\" should fail");
548 }
549
550 #[test]
551 fn test_from_string_no_char() {
552 DurationString::from_string(String::from("1234"))
553 .expect_err("parsing \"1234\" should fail");
554 }
555
556 #[test]
558 fn test_from_string() {
559 let d = DurationString::from_string(String::from("100ms"));
560 assert_eq!("100ms", format!("{}", d.unwrap()));
561 }
562
563 #[test]
564 fn test_display_trait() {
565 let d = DurationString::from(Duration::from_millis(100));
566 assert_eq!("100ms", format!("{d}"));
567 }
568
569 #[test]
570 fn test_from_duration() {
571 let d: String = DurationString::from(Duration::from_millis(100)).into();
572 assert_eq!(d, String::from("100ms"));
573 }
574
575 fn test_parse_string(input_str: &str, expected_duration: Duration) {
576 let d_fromstr: Duration = input_str
577 .parse::<DurationString>()
578 .expect("Parse with FromStr failed")
579 .into();
580 assert_eq!(d_fromstr, expected_duration, "FromStr");
581 let d_using_tryfrom: Duration = DurationString::try_from(input_str.to_owned())
582 .expect("Parse with TryFrom failed")
583 .into();
584 assert_eq!(d_using_tryfrom, expected_duration, "TryFrom");
585 }
586
587 #[test]
588 fn test_from_string_ms() {
589 test_parse_string("100ms", Duration::from_millis(100));
590 }
591
592 #[test]
593 fn test_from_string_us() {
594 test_parse_string("100us", Duration::from_micros(100));
595 }
596
597 #[test]
598 fn test_from_string_us_ms() {
599 test_parse_string("1ms100us", Duration::from_micros(1100));
600 }
601
602 #[test]
603 fn test_from_string_ns() {
604 test_parse_string("100ns", Duration::from_nanos(100));
605 }
606
607 #[test]
608 fn test_from_string_s() {
609 test_parse_string("1s", Duration::from_secs(1));
610 }
611
612 #[test]
613 fn test_from_string_m() {
614 test_parse_string("1m", Duration::from_secs(60));
615 }
616
617 #[test]
618 fn test_from_string_m_s() {
619 test_parse_string("1m 1s", Duration::from_secs(61));
620 }
621
622 #[test]
623 fn test_from_string_h() {
624 test_parse_string("1h", Duration::from_secs(3600));
625 }
626
627 #[test]
628 fn test_from_string_h_m() {
629 test_parse_string("1h30m", Duration::from_secs(5400));
630 }
631
632 #[test]
633 fn test_from_string_h_m2() {
634 test_parse_string("1h128m", Duration::from_secs(11280));
635 }
636
637 #[test]
638 fn test_from_string_d() {
639 test_parse_string("1d", Duration::from_secs(86_400));
640 }
641
642 #[test]
643 fn test_from_string_w() {
644 test_parse_string("1w", Duration::from_secs(604_800));
645 }
646
647 #[test]
648 fn test_from_string_w_s() {
649 test_parse_string("1w 1s", Duration::from_secs(604_801));
650 }
651
652 #[test]
653 fn test_from_string_y() {
654 test_parse_string("1y", Duration::from_secs(31_556_926));
655 }
656
657 #[test]
658 fn test_into_string_ms() {
659 let d: String = DurationString::try_from(String::from("100ms"))
660 .unwrap()
661 .into();
662 assert_eq!(d, "100ms");
663 }
664
665 #[test]
666 fn test_into_string_s() {
667 let d: String = DurationString::try_from(String::from("1s")).unwrap().into();
668 assert_eq!(d, "1s");
669 }
670
671 #[test]
672 fn test_into_string_m() {
673 let d: String = DurationString::try_from(String::from("1m")).unwrap().into();
674 assert_eq!(d, "1m");
675 }
676
677 #[test]
678 fn test_into_string_h() {
679 let d: String = DurationString::try_from(String::from("1h")).unwrap().into();
680 assert_eq!(d, "1h");
681 }
682
683 #[test]
684 fn test_into_string_d() {
685 let d: String = DurationString::try_from(String::from("1d")).unwrap().into();
686 assert_eq!(d, "1d");
687 }
688
689 #[test]
690 fn test_into_string_w() {
691 let d: String = DurationString::try_from(String::from("1w")).unwrap().into();
692 assert_eq!(d, "1w");
693 }
694
695 #[test]
696 fn test_into_string_y() {
697 let d: String = DurationString::try_from(String::from("1y")).unwrap().into();
698 assert_eq!(d, "1y");
699 }
700
701 #[test]
702 fn test_into_string_overflow_unit() {
703 let d: String = DurationString::try_from(String::from("1000ms"))
704 .unwrap()
705 .into();
706 assert_eq!(d, "1s");
707
708 let d: String = DurationString::try_from(String::from("60000ms"))
709 .unwrap()
710 .into();
711 assert_eq!(d, "1m");
712
713 let d: String = DurationString::try_from(String::from("61000ms"))
714 .unwrap()
715 .into();
716 assert_eq!(d, "61s");
717 }
718
719 #[test]
720 fn test_from_string_invalid_string() {
721 DurationString::try_from(String::from("1000x"))
722 .expect_err("Should have failed with invalid format");
723 }
724
725 #[test]
726 fn test_try_from_string_overflow_y() {
727 let result = DurationString::try_from(String::from("584554530873y"));
728 assert_eq!(result, Err(Error::Overflow));
729 }
730
731 #[test]
732 fn test_try_from_string_overflow_y_w() {
733 let result = DurationString::try_from(String::from("584554530872y 29w"));
734 assert_eq!(result, Err(Error::Overflow));
735 }
736
737 #[test]
738 fn test_eq() {
739 let duration = Duration::from_secs(1);
740 assert_eq!(DurationString::new(duration), DurationString::new(duration));
741 assert_eq!(DurationString::new(duration), duration);
742 assert_eq!(duration, DurationString::new(duration));
743 }
744
745 #[test]
746 fn test_ne() {
747 let a = Duration::from_secs(1);
748 let b = Duration::from_secs(2);
749 assert_ne!(DurationString::new(a), DurationString::new(b));
750 assert_ne!(DurationString::new(a), b);
751 assert_ne!(a, DurationString::new(b));
752 }
753
754 #[test]
755 fn test_lt() {
756 let a = Duration::from_secs(1);
757 let b = Duration::from_secs(2);
758 assert!(DurationString::new(a) < DurationString::new(b));
759 assert!(DurationString::new(a) < b);
760 assert!(a < DurationString::new(b));
761 }
762
763 #[test]
764 fn test_le() {
765 let a = Duration::from_secs(1);
766 let b = Duration::from_secs(2);
767 assert!(DurationString::new(a) <= DurationString::new(b));
768 assert!(DurationString::new(a) <= b);
769 assert!(a <= DurationString::new(b));
770 let a = Duration::from_secs(1);
771 let b = Duration::from_secs(1);
772 assert!(DurationString::new(a) <= DurationString::new(b));
773 assert!(DurationString::new(a) <= b);
774 assert!(a <= DurationString::new(b));
775 }
776
777 #[test]
778 fn test_gt() {
779 let a = Duration::from_secs(2);
780 let b = Duration::from_secs(1);
781 assert!(DurationString::new(a) > DurationString::new(b));
782 assert!(DurationString::new(a) > b);
783 assert!(a > DurationString::new(b));
784 }
785
786 #[test]
787 fn test_ge() {
788 let a = Duration::from_secs(2);
789 let b = Duration::from_secs(1);
790 assert!(DurationString::new(a) >= DurationString::new(b));
791 assert!(DurationString::new(a) >= b);
792 assert!(a >= DurationString::new(b));
793 let a = Duration::from_secs(1);
794 let b = Duration::from_secs(1);
795 assert!(DurationString::new(a) >= DurationString::new(b));
796 assert!(DurationString::new(a) >= b);
797 assert!(a >= DurationString::new(b));
798 }
799
800 #[test]
801 fn test_add() {
802 let a = Duration::from_secs(1);
803 let b = Duration::from_secs(1);
804 let result = a + b;
805 assert_eq!(
806 DurationString::new(a) + DurationString::new(b),
807 DurationString::new(result)
808 );
809 assert_eq!(DurationString::new(a) + b, DurationString::new(result));
810 assert_eq!(a + DurationString::new(b), result);
811 }
812
813 #[test]
814 fn test_add_assign() {
815 let a = Duration::from_secs(1);
816 let b = Duration::from_secs(1);
817 let result = a + b;
818 let mut duration_string_duration_string = DurationString::new(a);
819 duration_string_duration_string += DurationString::new(b);
820 let mut duration_string_duration = DurationString::new(a);
821 duration_string_duration += b;
822 let mut duration_duration_string = a;
823 duration_duration_string += DurationString::new(b);
824 assert_eq!(duration_string_duration_string, DurationString::new(result));
825 assert_eq!(duration_string_duration, DurationString::new(result));
826 assert_eq!(duration_duration_string, result);
827 }
828
829 #[test]
830 fn test_sub() {
831 let a = Duration::from_secs(1);
832 let b = Duration::from_secs(1);
833 let result = a - b;
834 assert_eq!(
835 DurationString::new(a) - DurationString::new(b),
836 DurationString::new(result)
837 );
838 assert_eq!(DurationString::new(a) - b, DurationString::new(result));
839 assert_eq!(a - DurationString::new(b), result);
840 }
841
842 #[test]
843 fn test_sub_assign() {
844 let a = Duration::from_secs(1);
845 let b = Duration::from_secs(1);
846 let result = a - b;
847 let mut duration_string_duration_string = DurationString::new(a);
848 duration_string_duration_string -= DurationString::new(b);
849 let mut duration_string_duration = DurationString::new(a);
850 duration_string_duration -= b;
851 let mut duration_duration_string = a;
852 duration_duration_string -= DurationString::new(b);
853 assert_eq!(duration_string_duration_string, DurationString::new(result));
854 assert_eq!(duration_string_duration, DurationString::new(result));
855 assert_eq!(duration_duration_string, result);
856 }
857
858 #[test]
859 fn test_mul() {
860 let a = 2u32;
861 let a_duration = DurationString::new(Duration::from_secs(a.into()));
862 let b = 4u32;
863 let b_duration = DurationString::new(Duration::from_secs(b.into()));
864 let result = DurationString::new(Duration::from_secs((a * b).into()));
865 assert_eq!(a_duration * b, result);
866 assert_eq!(a * b_duration, result);
867 }
868
869 #[test]
870 fn test_mul_assign() {
871 let a = 2u32;
872 let b = 4u32;
873 let result = DurationString::new(Duration::from_secs((a * b).into()));
874 let mut duration_string_u32 = DurationString::new(Duration::from_secs(a.into()));
875 duration_string_u32 *= b;
876 assert_eq!(duration_string_u32, result);
877 }
878
879 #[test]
880 fn test_div() {
881 let a = 8u32;
882 let a_duration = DurationString::new(Duration::from_secs(a.into()));
883 let b = 4u32;
884 let result = DurationString::new(Duration::from_secs((a / b).into()));
885 assert_eq!(a_duration / b, result);
886 }
887
888 #[test]
889 fn test_div_assign() {
890 let a = 8u32;
891 let b = 4u32;
892 let result = DurationString::new(Duration::from_secs((a / b).into()));
893 let mut duration_string_u32 = DurationString::new(Duration::from_secs(a.into()));
894 duration_string_u32 /= b;
895 assert_eq!(duration_string_u32, result);
896 }
897
898 #[test]
899 fn test_sum() {
900 let durations = [
901 Duration::from_secs(1),
902 Duration::from_secs(2),
903 Duration::from_secs(3),
904 Duration::from_secs(4),
905 Duration::from_secs(5),
906 Duration::from_secs(6),
907 Duration::from_secs(7),
908 Duration::from_secs(8),
909 Duration::from_secs(9),
910 ];
911 let result = DurationString::new(durations.iter().sum());
912 let durations = durations
913 .iter()
914 .map(|duration| Into::<DurationString>::into(*duration))
915 .collect::<Vec<_>>();
916 assert_eq!(durations.iter().sum::<DurationString>(), result);
917 assert_eq!(durations.into_iter().sum::<DurationString>(), result);
918 }
919}