1#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![allow(renamed_and_removed_lints)] #![allow(unknown_lints)] #![warn(missing_docs)]
7#![warn(noop_method_call)]
8#![warn(unreachable_pub)]
9#![warn(clippy::all)]
10#![deny(clippy::await_holding_lock)]
11#![deny(clippy::cargo_common_metadata)]
12#![deny(clippy::cast_lossless)]
13#![deny(clippy::checked_conversions)]
14#![warn(clippy::cognitive_complexity)]
15#![deny(clippy::debug_assert_with_mut_call)]
16#![deny(clippy::exhaustive_enums)]
17#![deny(clippy::exhaustive_structs)]
18#![deny(clippy::expl_impl_clone_on_copy)]
19#![deny(clippy::fallible_impl_from)]
20#![deny(clippy::implicit_clone)]
21#![deny(clippy::large_stack_arrays)]
22#![warn(clippy::manual_ok_or)]
23#![deny(clippy::missing_docs_in_private_items)]
24#![warn(clippy::needless_borrow)]
25#![warn(clippy::needless_pass_by_value)]
26#![warn(clippy::option_option)]
27#![deny(clippy::print_stderr)]
28#![deny(clippy::print_stdout)]
29#![warn(clippy::rc_buffer)]
30#![deny(clippy::ref_option_ref)]
31#![warn(clippy::semicolon_if_nothing_returned)]
32#![warn(clippy::trait_duplication_in_bounds)]
33#![deny(clippy::unchecked_duration_subtraction)]
34#![deny(clippy::unnecessary_wraps)]
35#![warn(clippy::unseparated_literal_suffix)]
36#![deny(clippy::unwrap_used)]
37#![deny(clippy::mod_module_files)]
38#![allow(clippy::let_unit_value)] #![allow(clippy::uninlined_format_args)]
40#![allow(clippy::significant_drop_in_scrutinee)] #![allow(clippy::result_large_err)] #![allow(clippy::needless_raw_string_hashes)] #![allow(clippy::needless_lifetimes)] use derive_more::{Add, Display, Div, From, FromStr, Mul};
47
48use std::time::Duration;
49use thiserror::Error;
50
51#[cfg(feature = "memquota-memcost")]
52use {derive_deftly::Deftly, tor_memquota::derive_deftly_template_HasMemoryCost};
53
54#[derive(Debug, Clone, PartialEq, Eq, Error)]
56#[non_exhaustive]
57pub enum Error {
58 #[error("Value {0} was below the lower bound {1} for this type")]
60 BelowLowerBound(i32, i32),
61 #[error("Value {0} was above the lower bound {1} for this type")]
63 AboveUpperBound(i32, i32),
64 #[error("Tried to convert a negative value to an unsigned type")]
66 Negative,
67 #[error("Value could not be represented as an i32")]
70 Unrepresentable,
71 #[error("Integer overflow")]
73 Overflow,
74 #[error("No value is valid for this type")]
76 Uninhabited,
77}
78
79#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
92#[cfg_attr(
93 feature = "memquota-memcost",
94 derive(Deftly),
95 derive_deftly(HasMemoryCost)
96)]
97pub struct BoundedInt32<const LOWER: i32, const UPPER: i32> {
98 value: i32,
100}
101
102impl<const LOWER: i32, const UPPER: i32> BoundedInt32<LOWER, UPPER> {
103 pub const LOWER: i32 = LOWER;
105 pub const UPPER: i32 = UPPER;
107
108 fn unchecked_new(value: i32) -> Self {
110 assert!(LOWER <= UPPER); BoundedInt32 { value }
113 }
114
115 pub const fn lower(&self) -> i32 {
119 LOWER
120 }
121
122 pub const fn upper(&self) -> i32 {
126 UPPER
127 }
128
129 pub fn get(&self) -> i32 {
134 self.value
135 }
136
137 pub fn saturating_new(val: i32) -> Self {
141 Self::unchecked_new(Self::clamp(val))
142 }
143
144 pub fn checked_new(val: i32) -> Result<Self, Error> {
147 if val > UPPER {
148 Err(Error::AboveUpperBound(val, UPPER))
149 } else if val < LOWER {
150 Err(Error::BelowLowerBound(val, LOWER))
151 } else {
152 Ok(BoundedInt32::unchecked_new(val))
153 }
154 }
155
156 fn clamp(val: i32) -> i32 {
158 Ord::clamp(val, LOWER, UPPER)
159 }
160
161 pub fn saturating_from(val: i32) -> Self {
168 Self::unchecked_new(Self::clamp(val))
169 }
170
171 pub fn saturating_from_str(s: &str) -> Result<Self, Error> {
178 if UPPER < LOWER {
179 return Err(Error::Uninhabited);
181 }
182 let val: i32 = s.parse().map_err(|_| Error::Unrepresentable)?;
183 Ok(Self::saturating_from(val))
184 }
185}
186
187impl<const L: i32, const U: i32> std::fmt::Display for BoundedInt32<L, U> {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 write!(f, "{}", self.value)
190 }
191}
192
193impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for i32 {
194 fn from(val: BoundedInt32<L, U>) -> i32 {
195 val.value
196 }
197}
198
199impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for f64 {
200 fn from(val: BoundedInt32<L, U>) -> f64 {
201 val.value.into()
202 }
203}
204
205impl<const L: i32, const H: i32> TryFrom<i32> for BoundedInt32<L, H> {
206 type Error = Error;
207 fn try_from(val: i32) -> Result<Self, Self::Error> {
208 Self::checked_new(val)
209 }
210}
211
212impl<const L: i32, const H: i32> std::str::FromStr for BoundedInt32<L, H> {
213 type Err = Error;
214 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
215 Self::checked_new(s.parse().map_err(|_| Error::Unrepresentable)?)
216 }
217}
218
219impl From<BoundedInt32<0, 1>> for bool {
220 fn from(val: BoundedInt32<0, 1>) -> bool {
221 val.value == 1
222 }
223}
224
225impl From<BoundedInt32<0, 255>> for u8 {
226 fn from(val: BoundedInt32<0, 255>) -> u8 {
227 val.value as u8
228 }
229}
230
231impl<const L: i32, const H: i32> From<BoundedInt32<L, H>> for u32 {
232 fn from(val: BoundedInt32<L, H>) -> u32 {
233 val.value as u32
234 }
235}
236
237impl<const L: i32, const H: i32> TryFrom<BoundedInt32<L, H>> for u64 {
238 type Error = Error;
239 fn try_from(val: BoundedInt32<L, H>) -> Result<Self, Self::Error> {
240 if val.value < 0 {
241 Err(Error::Negative)
242 } else {
243 Ok(val.value as u64)
244 }
245 }
246}
247
248impl<const L: i32, const H: i32> TryFrom<BoundedInt32<L, H>> for usize {
249 type Error = Error;
250 fn try_from(val: BoundedInt32<L, H>) -> Result<Self, Self::Error> {
251 if val.value < 0 {
252 Err(Error::Negative)
253 } else {
254 Ok(val.value as usize)
255 }
256 }
257}
258
259#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
264pub struct Percentage<T: Copy + Into<f64>> {
265 value: T,
267}
268
269impl<T: Copy + Into<f64>> Percentage<T> {
270 pub fn new(value: T) -> Self {
272 Self { value }
273 }
274
275 pub fn as_fraction(self) -> f64 {
289 self.value.into() / 100.0
290 }
291
292 pub fn as_percent(self) -> T {
305 self.value
306 }
307}
308
309impl<const H: i32, const L: i32> TryFrom<i32> for Percentage<BoundedInt32<H, L>> {
310 type Error = Error;
311 fn try_from(v: i32) -> Result<Self, Error> {
312 Ok(Percentage::new(v.try_into()?))
313 }
314}
315
316#[derive(
320 Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
321)]
322pub struct IntegerMilliseconds<T> {
326 value: T,
328}
329
330impl<T> IntegerMilliseconds<T> {
331 pub fn new(value: T) -> Self {
333 IntegerMilliseconds { value }
334 }
335
336 pub fn as_millis(self) -> T {
341 self.value
342 }
343
344 pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMilliseconds<U>, E>
356 where
357 F: FnOnce(T) -> Result<U, E>,
358 {
359 Ok(IntegerMilliseconds::new(f(self.value)?))
360 }
361}
362
363impl<T: TryInto<u64>> TryFrom<IntegerMilliseconds<T>> for Duration {
364 type Error = <T as TryInto<u64>>::Error;
365 fn try_from(val: IntegerMilliseconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
366 Ok(Self::from_millis(val.value.try_into()?))
367 }
368}
369
370impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMilliseconds<BoundedInt32<H, L>> {
371 type Error = Error;
372 fn try_from(v: i32) -> Result<Self, Error> {
373 Ok(IntegerMilliseconds::new(v.try_into()?))
374 }
375}
376
377#[derive(
378 Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
379)]
380pub struct IntegerSeconds<T> {
384 value: T,
386}
387
388impl<T> IntegerSeconds<T> {
389 pub fn new(value: T) -> Self {
391 IntegerSeconds { value }
392 }
393
394 pub fn as_secs(self) -> T {
399 self.value
400 }
401
402 pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerSeconds<U>, E>
412 where
413 F: FnOnce(T) -> Result<U, E>,
414 {
415 Ok(IntegerSeconds::new(f(self.value)?))
416 }
417}
418
419impl<T: TryInto<u64>> TryFrom<IntegerSeconds<T>> for Duration {
420 type Error = <T as TryInto<u64>>::Error;
421 fn try_from(val: IntegerSeconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
422 Ok(Self::from_secs(val.value.try_into()?))
423 }
424}
425
426impl<const H: i32, const L: i32> TryFrom<i32> for IntegerSeconds<BoundedInt32<H, L>> {
427 type Error = Error;
428 fn try_from(v: i32) -> Result<Self, Error> {
429 Ok(IntegerSeconds::new(v.try_into()?))
430 }
431}
432
433#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
434pub struct IntegerMinutes<T> {
438 value: T,
440}
441
442impl<T> IntegerMinutes<T> {
443 pub fn new(value: T) -> Self {
445 IntegerMinutes { value }
446 }
447
448 pub fn as_minutes(self) -> T {
453 self.value
454 }
455
456 pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMinutes<U>, E>
466 where
467 F: FnOnce(T) -> Result<U, E>,
468 {
469 Ok(IntegerMinutes::new(f(self.value)?))
470 }
471}
472
473impl<T: TryInto<u64>> TryFrom<IntegerMinutes<T>> for Duration {
474 type Error = Error;
475 fn try_from(val: IntegerMinutes<T>) -> Result<Self, Error> {
476 const SECONDS_PER_MINUTE: u64 = 60;
478 let minutes: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
479 let seconds = minutes
480 .checked_mul(SECONDS_PER_MINUTE)
481 .ok_or(Error::Overflow)?;
482 Ok(Self::from_secs(seconds))
483 }
484}
485
486impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMinutes<BoundedInt32<H, L>> {
487 type Error = Error;
488 fn try_from(v: i32) -> Result<Self, Error> {
489 Ok(IntegerMinutes::new(v.try_into()?))
490 }
491}
492
493#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
494pub struct IntegerDays<T> {
498 value: T,
500}
501
502impl<T> IntegerDays<T> {
503 pub fn new(value: T) -> Self {
505 IntegerDays { value }
506 }
507
508 pub fn as_days(self) -> T {
513 self.value
514 }
515
516 pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerDays<U>, E>
526 where
527 F: FnOnce(T) -> Result<U, E>,
528 {
529 Ok(IntegerDays::new(f(self.value)?))
530 }
531}
532
533impl<T: TryInto<u64>> TryFrom<IntegerDays<T>> for Duration {
534 type Error = Error;
535 fn try_from(val: IntegerDays<T>) -> Result<Self, Error> {
536 const SECONDS_PER_DAY: u64 = 86400;
538 let days: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
539 let seconds = days.checked_mul(SECONDS_PER_DAY).ok_or(Error::Overflow)?;
540 Ok(Self::from_secs(seconds))
541 }
542}
543
544impl<const H: i32, const L: i32> TryFrom<i32> for IntegerDays<BoundedInt32<H, L>> {
545 type Error = Error;
546 fn try_from(v: i32) -> Result<Self, Error> {
547 Ok(IntegerDays::new(v.try_into()?))
548 }
549}
550
551#[derive(Clone, Copy, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
555pub struct SendMeVersion(u8);
556
557impl SendMeVersion {
558 pub fn new(value: u8) -> Self {
560 SendMeVersion(value)
561 }
562
563 pub fn get(&self) -> u8 {
565 self.0
566 }
567}
568
569impl TryFrom<i32> for SendMeVersion {
570 type Error = Error;
571 fn try_from(v: i32) -> Result<Self, Error> {
572 let val_u8 = BoundedInt32::<0, 255>::checked_new(v)?;
573 Ok(SendMeVersion::new(val_u8.get() as u8))
574 }
575}
576
577#[cfg(test)]
578mod tests {
579 #![allow(clippy::unwrap_used)]
580 use float_cmp::assert_approx_eq;
581
582 use super::*;
583
584 type TestFoo = BoundedInt32<1, 5>;
585 type TestBar = BoundedInt32<-45, 17>;
586
587 #[test]
589 fn entire_range_parsed() {
590 let x: TestFoo = "1".parse().unwrap();
591 assert!(x.get() == 1);
592 let x: TestFoo = "2".parse().unwrap();
593 assert!(x.get() == 2);
594 let x: TestFoo = "3".parse().unwrap();
595 assert!(x.get() == 3);
596 let x: TestFoo = "4".parse().unwrap();
597 assert!(x.get() == 4);
598 let x: TestFoo = "5".parse().unwrap();
599 assert!(x.get() == 5);
600 }
601
602 #[test]
603 fn saturating() {
604 let x: TestFoo = TestFoo::saturating_new(1000);
605 let x_val: i32 = x.into();
606 assert!(x_val == TestFoo::UPPER);
607 let x: TestFoo = TestFoo::saturating_new(0);
608 let x_val: i32 = x.into();
609 assert!(x_val == TestFoo::LOWER);
610 }
611 #[test]
612 fn saturating_string() {
613 let x: TestFoo = TestFoo::saturating_from_str("1000").unwrap();
614 let x_val: i32 = x.into();
615 assert!(x_val == TestFoo::UPPER);
616 let x: TestFoo = TestFoo::saturating_from_str("0").unwrap();
617 let x_val: i32 = x.into();
618 assert!(x_val == TestFoo::LOWER);
619 }
620
621 #[test]
622 #[should_panic]
623 fn uninhabited_saturating_new() {
624 let _: BoundedInt32<10, 5> = BoundedInt32::saturating_new(7);
626 }
627
628 #[test]
629 fn uninhabited_from_string() {
630 let v: Result<BoundedInt32<10, 5>, Error> = BoundedInt32::saturating_from_str("7");
631 assert!(matches!(v, Err(Error::Uninhabited)));
632 }
633
634 #[test]
635 fn errors_correct() {
636 let x: Result<TestBar, Error> = "1000".parse();
637 assert!(x.unwrap_err() == Error::AboveUpperBound(1000, TestBar::UPPER));
638 let x: Result<TestBar, Error> = "-1000".parse();
639 assert!(x.unwrap_err() == Error::BelowLowerBound(-1000, TestBar::LOWER));
640 let x: Result<TestBar, Error> = "xyz".parse();
641 assert!(x.unwrap_err() == Error::Unrepresentable);
642 }
643
644 #[test]
645 fn display() {
646 let v = BoundedInt32::<99, 1000>::checked_new(345).unwrap();
647 assert_eq!(v.to_string(), "345".to_string());
648 }
649
650 #[test]
651 #[should_panic]
652 fn checked_too_high() {
653 let _: TestBar = "1000".parse().unwrap();
654 }
655
656 #[test]
657 #[should_panic]
658 fn checked_too_low() {
659 let _: TestBar = "-46".parse().unwrap();
660 }
661
662 #[test]
663 fn bounded_to_u64() {
664 let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
665 let u: u64 = b.try_into().unwrap();
666 assert_eq!(u, 77);
667
668 let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(-77).unwrap();
669 let u: Result<u64, Error> = b.try_into();
670 assert!(u.is_err());
671 }
672
673 #[test]
674 fn bounded_to_f64() {
675 let x: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
676 let f: f64 = x.into();
677 assert_approx_eq!(f64, f, 77.0);
678 }
679
680 #[test]
681 fn bounded_from_i32() {
682 let x: Result<BoundedInt32<-100, 100>, _> = 50.try_into();
683 let y: i32 = x.unwrap().into();
684 assert_eq!(y, 50);
685
686 let x: Result<BoundedInt32<-100, 100>, _> = 1000.try_into();
687 assert!(x.is_err());
688 }
689
690 #[test]
691 fn into_bool() {
692 let zero: BoundedInt32<0, 1> = BoundedInt32::saturating_from(0);
693 let one: BoundedInt32<0, 1> = BoundedInt32::saturating_from(1);
694
695 let f: bool = zero.into();
696 let t: bool = one.into();
697 assert!(!f);
698 assert!(t);
699 }
700
701 #[test]
702 fn into_u8() {
703 let zero: BoundedInt32<0, 255> = BoundedInt32::saturating_from(0);
704 let one: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1);
705 let ninety: BoundedInt32<0, 255> = BoundedInt32::saturating_from(90);
706 let max: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1000);
707
708 let a: u8 = zero.into();
709 let b: u8 = one.into();
710 let c: u8 = ninety.into();
711 let d: u8 = max.into();
712
713 assert_eq!(a, 0);
714 assert_eq!(b, 1);
715 assert_eq!(c, 90);
716 assert_eq!(d, 255);
717 }
718
719 #[test]
720 fn into_u32() {
721 let zero: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(0);
722 let one: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1);
723 let ninety: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(90);
724 let max: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1000);
725
726 assert_eq!(u32::from(zero), 0);
727 assert_eq!(u32::from(one), 1);
728 assert_eq!(u32::from(ninety), 90);
729 assert_eq!(u32::from(max), 1000);
730
731 let zero: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(0);
732 let one: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1);
733 let ninety: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(90);
734 let max: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1000);
735
736 assert_eq!(u32::from(zero), 1);
737 assert_eq!(u32::from(one), 1);
738 assert_eq!(u32::from(ninety), 90);
739 assert_eq!(u32::from(max), 1000);
740 }
741
742 #[test]
743 fn try_into_usize() {
744 let b0: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(0);
745 let b100: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(100);
746 let bn5: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(-5);
747 assert_eq!(usize::try_from(b0), Ok(0_usize));
748 assert_eq!(usize::try_from(b100), Ok(100_usize));
749 assert_eq!(usize::try_from(bn5), Err(Error::Negative));
750 }
751
752 #[test]
753 fn percents() {
754 type Pct = Percentage<u8>;
755 let p = Pct::new(100);
756 assert_eq!(p.as_percent(), 100);
757 assert_approx_eq!(f64, p.as_fraction(), 1.0);
758
759 let p = Pct::new(0);
760 assert_eq!(p.as_percent(), 0);
761 assert_approx_eq!(f64, p.as_fraction(), 0.0);
762
763 let p = Pct::new(25);
764 assert_eq!(p.as_percent(), 25);
765 assert_eq!(p.clone(), p);
766 assert_approx_eq!(f64, p.as_fraction(), 0.25);
767
768 type BPct = Percentage<BoundedInt32<0, 100>>;
769 assert_eq!(BPct::try_from(99).unwrap().as_percent().get(), 99);
770 }
771
772 #[test]
773 fn milliseconds() {
774 type Msec = IntegerMilliseconds<i32>;
775
776 let ms = Msec::new(500);
777 let d: Result<Duration, _> = ms.try_into();
778 assert_eq!(d.unwrap(), Duration::from_millis(500));
779 assert_eq!(Duration::try_from(ms * 2).unwrap(), Duration::from_secs(1));
780
781 let ms = Msec::new(-100);
782 let d: Result<Duration, _> = ms.try_into();
783 assert!(d.is_err());
784
785 type BMSec = IntegerMilliseconds<BoundedInt32<0, 1000>>;
786 let half_sec = BMSec::try_from(500).unwrap();
787 assert_eq!(
788 Duration::try_from(half_sec).unwrap(),
789 Duration::from_millis(500)
790 );
791 assert!(BMSec::try_from(1001).is_err());
792 }
793
794 #[test]
795 fn seconds() {
796 type Sec = IntegerSeconds<i32>;
797
798 let ms = Sec::new(500);
799 let d: Result<Duration, _> = ms.try_into();
800 assert_eq!(d.unwrap(), Duration::from_secs(500));
801
802 let ms = Sec::new(-100);
803 let d: Result<Duration, _> = ms.try_into();
804 assert!(d.is_err());
805
806 type BSec = IntegerSeconds<BoundedInt32<0, 3600>>;
807 let half_hour = BSec::try_from(1800).unwrap();
808 assert_eq!(
809 Duration::try_from(half_hour).unwrap(),
810 Duration::from_secs(1800)
811 );
812 assert!(BSec::try_from(9999).is_err());
813 assert_eq!(half_hour.clone(), half_hour);
814 }
815
816 #[test]
817 fn minutes() {
818 type Min = IntegerMinutes<i32>;
819
820 let t = Min::new(500);
821 let d: Duration = t.try_into().unwrap();
822 assert_eq!(d, Duration::from_secs(500 * 60));
823
824 let t = Min::new(-100);
825 let d: Result<Duration, _> = t.try_into();
826 assert_eq!(d, Err(Error::Overflow));
827
828 let t = IntegerMinutes::<u64>::new(u64::MAX);
829 let d: Result<Duration, _> = t.try_into();
830 assert_eq!(d, Err(Error::Overflow));
831
832 type BMin = IntegerMinutes<BoundedInt32<10, 30>>;
833 assert_eq!(
834 BMin::new(17_i32.try_into().unwrap()),
835 BMin::try_from(17).unwrap()
836 );
837 }
838
839 #[test]
840 fn days() {
841 type Days = IntegerDays<i32>;
842
843 let t = Days::new(500);
844 let d: Duration = t.try_into().unwrap();
845 assert_eq!(d, Duration::from_secs(500 * 86400));
846
847 let t = Days::new(-100);
848 let d: Result<Duration, _> = t.try_into();
849 assert_eq!(d, Err(Error::Overflow));
850
851 let t = IntegerDays::<u64>::new(u64::MAX);
852 let d: Result<Duration, _> = t.try_into();
853 assert_eq!(d, Err(Error::Overflow));
854
855 type BDays = IntegerDays<BoundedInt32<10, 30>>;
856 assert_eq!(
857 BDays::new(17_i32.try_into().unwrap()),
858 BDays::try_from(17).unwrap()
859 );
860 }
861
862 #[test]
863 fn sendme() {
864 let smv = SendMeVersion::new(5);
865 assert_eq!(smv.get(), 5);
866 assert_eq!(smv.clone().get(), 5);
867 assert_eq!(smv, SendMeVersion::try_from(5).unwrap());
868 }
869}