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