1#![cfg_attr(docsrs, feature(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_time_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)] #![allow(mismatched_lifetime_syntaxes)] #![allow(clippy::collapsible_if)] #![deny(clippy::unused_async)]
47use derive_more::{Add, Display, Div, From, FromStr, Mul};
50
51use serde::{Deserialize, Serialize};
52use std::time::Duration;
53use thiserror::Error;
54
55#[cfg(feature = "memquota-memcost")]
56use {derive_deftly::Deftly, tor_memquota::derive_deftly_template_HasMemoryCost};
57
58#[derive(Debug, Clone, PartialEq, Eq, Error)]
60#[non_exhaustive]
61pub enum Error {
62 #[error("Value {0} was below the lower bound {1} for this type")]
64 BelowLowerBound(i32, i32),
65 #[error("Value {0} was above the lower bound {1} for this type")]
67 AboveUpperBound(i32, i32),
68 #[error("Tried to convert a negative value to an unsigned type")]
70 Negative,
71 #[error("Value could not be represented as an i32")]
74 Unrepresentable,
75 #[error("Integer overflow")]
77 Overflow,
78}
79
80#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
97#[cfg_attr(
98 feature = "memquota-memcost",
99 derive(Deftly),
100 derive_deftly(HasMemoryCost)
101)]
102pub struct BoundedInt32<const LOWER: i32, const UPPER: i32> {
103 value: i32,
105}
106
107impl<const LOWER: i32, const UPPER: i32> BoundedInt32<LOWER, UPPER> {
108 pub const LOWER: i32 = LOWER;
110 pub const UPPER: i32 = UPPER;
112
113 fn unchecked_new(value: i32) -> Self {
115 const { assert!(LOWER <= UPPER) };
118
119 BoundedInt32 { value }
120 }
121
122 pub const fn lower(&self) -> i32 {
126 LOWER
127 }
128
129 pub const fn upper(&self) -> i32 {
133 UPPER
134 }
135
136 pub fn get(&self) -> i32 {
141 self.value
142 }
143
144 pub fn get_u32(&self) -> u32 {
151 const { assert!(LOWER >= 0) };
152 self.value as u32
153 }
154
155 pub fn saturating_new(val: i32) -> Self {
159 Self::unchecked_new(Self::clamp(val))
160 }
161
162 pub fn checked_new(val: i32) -> Result<Self, Error> {
165 if val > UPPER {
166 Err(Error::AboveUpperBound(val, UPPER))
167 } else if val < LOWER {
168 Err(Error::BelowLowerBound(val, LOWER))
169 } else {
170 Ok(BoundedInt32::unchecked_new(val))
171 }
172 }
173
174 fn clamp(val: i32) -> i32 {
176 Ord::clamp(val, LOWER, UPPER)
177 }
178
179 pub fn saturating_from(val: i32) -> Self {
186 Self::unchecked_new(Self::clamp(val))
187 }
188
189 pub fn saturating_from_str(s: &str) -> Result<Self, Error> {
196 let val: i32 = s.parse().map_err(|_| Error::Unrepresentable)?;
197 Ok(Self::saturating_from(val))
198 }
199}
200
201impl<const L: i32, const U: i32> std::fmt::Display for BoundedInt32<L, U> {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 write!(f, "{}", self.value)
204 }
205}
206
207impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for i32 {
208 fn from(val: BoundedInt32<L, U>) -> i32 {
209 val.value
210 }
211}
212
213impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for f64 {
214 fn from(val: BoundedInt32<L, U>) -> f64 {
215 val.value.into()
216 }
217}
218
219impl<const L: i32, const H: i32> TryFrom<i32> for BoundedInt32<L, H> {
220 type Error = Error;
221 fn try_from(val: i32) -> Result<Self, Self::Error> {
222 Self::checked_new(val)
223 }
224}
225
226impl<const L: i32, const H: i32> std::str::FromStr for BoundedInt32<L, H> {
227 type Err = Error;
228 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
229 Self::checked_new(s.parse().map_err(|_| Error::Unrepresentable)?)
230 }
231}
232
233impl From<BoundedInt32<0, 1>> for bool {
234 fn from(val: BoundedInt32<0, 1>) -> bool {
235 val.value == 1
236 }
237}
238
239impl From<BoundedInt32<0, 255>> for u8 {
240 fn from(val: BoundedInt32<0, 255>) -> u8 {
241 val.value as u8
242 }
243}
244
245impl<const L: i32, const H: i32> From<BoundedInt32<L, H>> for u32 {
246 fn from(val: BoundedInt32<L, H>) -> u32 {
247 val.value as u32
248 }
249}
250
251impl<const L: i32, const H: i32> TryFrom<BoundedInt32<L, H>> for u64 {
252 type Error = Error;
253 fn try_from(val: BoundedInt32<L, H>) -> Result<Self, Self::Error> {
254 if val.value < 0 {
255 Err(Error::Negative)
256 } else {
257 Ok(val.value as u64)
258 }
259 }
260}
261
262impl<const L: i32, const H: i32> TryFrom<BoundedInt32<L, H>> for usize {
263 type Error = Error;
264 fn try_from(val: BoundedInt32<L, H>) -> Result<Self, Self::Error> {
265 if val.value < 0 {
266 Err(Error::Negative)
267 } else {
268 Ok(val.value as usize)
269 }
270 }
271}
272
273#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
278pub struct Percentage<T: Copy + Into<f64>> {
279 value: T,
281}
282
283impl<T: Copy + Into<f64>> Percentage<T> {
284 pub fn new(value: T) -> Self {
286 Self { value }
287 }
288
289 pub fn as_fraction(self) -> f64 {
303 self.value.into() / 100.0
304 }
305
306 pub fn as_percent(self) -> T {
319 self.value
320 }
321}
322
323impl<const H: i32, const L: i32> TryFrom<i32> for Percentage<BoundedInt32<H, L>> {
324 type Error = Error;
325 fn try_from(v: i32) -> Result<Self, Error> {
326 Ok(Percentage::new(v.try_into()?))
327 }
328}
329
330#[derive(
334 Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
335)]
336pub struct IntegerMilliseconds<T> {
340 value: T,
342}
343
344impl<T> IntegerMilliseconds<T> {
345 pub fn new(value: T) -> Self {
347 IntegerMilliseconds { value }
348 }
349
350 pub fn as_millis(self) -> T {
355 self.value
356 }
357
358 pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMilliseconds<U>, E>
370 where
371 F: FnOnce(T) -> Result<U, E>,
372 {
373 Ok(IntegerMilliseconds::new(f(self.value)?))
374 }
375}
376
377impl<T: TryInto<u64>> TryFrom<IntegerMilliseconds<T>> for Duration {
378 type Error = <T as TryInto<u64>>::Error;
379 fn try_from(val: IntegerMilliseconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
380 Ok(Self::from_millis(val.value.try_into()?))
381 }
382}
383
384impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMilliseconds<BoundedInt32<H, L>> {
385 type Error = Error;
386 fn try_from(v: i32) -> Result<Self, Error> {
387 Ok(IntegerMilliseconds::new(v.try_into()?))
388 }
389}
390
391#[derive(
392 Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
393)]
394pub struct IntegerSeconds<T> {
398 value: T,
400}
401
402impl<T> IntegerSeconds<T> {
403 pub fn new(value: T) -> Self {
405 IntegerSeconds { value }
406 }
407
408 pub fn as_secs(self) -> T {
413 self.value
414 }
415
416 pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerSeconds<U>, E>
426 where
427 F: FnOnce(T) -> Result<U, E>,
428 {
429 Ok(IntegerSeconds::new(f(self.value)?))
430 }
431}
432
433impl<T: TryInto<u64>> TryFrom<IntegerSeconds<T>> for Duration {
434 type Error = <T as TryInto<u64>>::Error;
435 fn try_from(val: IntegerSeconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
436 Ok(Self::from_secs(val.value.try_into()?))
437 }
438}
439
440impl<const H: i32, const L: i32> TryFrom<i32> for IntegerSeconds<BoundedInt32<H, L>> {
441 type Error = Error;
442 fn try_from(v: i32) -> Result<Self, Error> {
443 Ok(IntegerSeconds::new(v.try_into()?))
444 }
445}
446
447#[derive(Deserialize, Serialize)] #[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
449pub struct IntegerMinutes<T> {
453 value: T,
455}
456
457impl<T> IntegerMinutes<T> {
458 pub fn new(value: T) -> Self {
460 IntegerMinutes { value }
461 }
462
463 pub fn as_minutes(self) -> T {
468 self.value
469 }
470
471 pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMinutes<U>, E>
481 where
482 F: FnOnce(T) -> Result<U, E>,
483 {
484 Ok(IntegerMinutes::new(f(self.value)?))
485 }
486}
487
488impl<T: TryInto<u64>> TryFrom<IntegerMinutes<T>> for Duration {
489 type Error = Error;
490 fn try_from(val: IntegerMinutes<T>) -> Result<Self, Error> {
491 const SECONDS_PER_MINUTE: u64 = 60;
493 let minutes: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
494 let seconds = minutes
495 .checked_mul(SECONDS_PER_MINUTE)
496 .ok_or(Error::Overflow)?;
497 Ok(Self::from_secs(seconds))
498 }
499}
500
501impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMinutes<BoundedInt32<H, L>> {
502 type Error = Error;
503 fn try_from(v: i32) -> Result<Self, Error> {
504 Ok(IntegerMinutes::new(v.try_into()?))
505 }
506}
507
508#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
509pub struct IntegerDays<T> {
513 value: T,
515}
516
517impl<T> IntegerDays<T> {
518 pub fn new(value: T) -> Self {
520 IntegerDays { value }
521 }
522
523 pub fn as_days(self) -> T {
528 self.value
529 }
530
531 pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerDays<U>, E>
541 where
542 F: FnOnce(T) -> Result<U, E>,
543 {
544 Ok(IntegerDays::new(f(self.value)?))
545 }
546}
547
548impl<T: TryInto<u64>> TryFrom<IntegerDays<T>> for Duration {
549 type Error = Error;
550 fn try_from(val: IntegerDays<T>) -> Result<Self, Error> {
551 const SECONDS_PER_DAY: u64 = 86400;
553 let days: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
554 let seconds = days.checked_mul(SECONDS_PER_DAY).ok_or(Error::Overflow)?;
555 Ok(Self::from_secs(seconds))
556 }
557}
558
559impl<const H: i32, const L: i32> TryFrom<i32> for IntegerDays<BoundedInt32<H, L>> {
560 type Error = Error;
561 fn try_from(v: i32) -> Result<Self, Error> {
562 Ok(IntegerDays::new(v.try_into()?))
563 }
564}
565
566#[derive(Clone, Copy, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
570pub struct SendMeVersion(u8);
571
572impl SendMeVersion {
573 pub fn new(value: u8) -> Self {
575 SendMeVersion(value)
576 }
577
578 pub fn get(&self) -> u8 {
580 self.0
581 }
582}
583
584impl TryFrom<i32> for SendMeVersion {
585 type Error = Error;
586 fn try_from(v: i32) -> Result<Self, Error> {
587 let val_u8 = BoundedInt32::<0, 255>::checked_new(v)?;
588 Ok(SendMeVersion::new(val_u8.get() as u8))
589 }
590}
591
592#[cfg(doc)]
598#[doc(hidden)]
599mod compile_fail_tests {
600 fn uninhabited_saturating_new() {}
605
606 fn uninhabited_from_string() {}
611}
612
613#[cfg(test)]
614mod tests {
615 #![allow(clippy::unwrap_used)]
616 use float_cmp::assert_approx_eq;
617
618 use super::*;
619
620 type TestFoo = BoundedInt32<1, 5>;
621 type TestBar = BoundedInt32<-45, 17>;
622
623 #[test]
625 fn entire_range_parsed() {
626 let x: TestFoo = "1".parse().unwrap();
627 assert!(x.get() == 1);
628 let x: TestFoo = "2".parse().unwrap();
629 assert!(x.get() == 2);
630 let x: TestFoo = "3".parse().unwrap();
631 assert!(x.get() == 3);
632 let x: TestFoo = "4".parse().unwrap();
633 assert!(x.get() == 4);
634 let x: TestFoo = "5".parse().unwrap();
635 assert!(x.get() == 5);
636 }
637
638 #[test]
639 fn saturating() {
640 let x: TestFoo = TestFoo::saturating_new(1000);
641 let x_val: i32 = x.into();
642 assert!(x_val == TestFoo::UPPER);
643 let x: TestFoo = TestFoo::saturating_new(0);
644 let x_val: i32 = x.into();
645 assert!(x_val == TestFoo::LOWER);
646 }
647 #[test]
648 fn saturating_string() {
649 let x: TestFoo = TestFoo::saturating_from_str("1000").unwrap();
650 let x_val: i32 = x.into();
651 assert!(x_val == TestFoo::UPPER);
652 let x: TestFoo = TestFoo::saturating_from_str("0").unwrap();
653 let x_val: i32 = x.into();
654 assert!(x_val == TestFoo::LOWER);
655 }
656
657 #[test]
658 fn errors_correct() {
659 let x: Result<TestBar, Error> = "1000".parse();
660 assert!(x.unwrap_err() == Error::AboveUpperBound(1000, TestBar::UPPER));
661 let x: Result<TestBar, Error> = "-1000".parse();
662 assert!(x.unwrap_err() == Error::BelowLowerBound(-1000, TestBar::LOWER));
663 let x: Result<TestBar, Error> = "xyz".parse();
664 assert!(x.unwrap_err() == Error::Unrepresentable);
665 }
666
667 #[test]
668 fn display() {
669 let v = BoundedInt32::<99, 1000>::checked_new(345).unwrap();
670 assert_eq!(v.to_string(), "345".to_string());
671 }
672
673 #[test]
674 #[should_panic]
675 fn checked_too_high() {
676 let _: TestBar = "1000".parse().unwrap();
677 }
678
679 #[test]
680 #[should_panic]
681 fn checked_too_low() {
682 let _: TestBar = "-46".parse().unwrap();
683 }
684
685 #[test]
686 fn bounded_to_u64() {
687 let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
688 let u: u64 = b.try_into().unwrap();
689 assert_eq!(u, 77);
690
691 let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(-77).unwrap();
692 let u: Result<u64, Error> = b.try_into();
693 assert!(u.is_err());
694 }
695
696 #[test]
697 fn bounded_to_f64() {
698 let x: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
699 let f: f64 = x.into();
700 assert_approx_eq!(f64, f, 77.0);
701 }
702
703 #[test]
704 fn bounded_from_i32() {
705 let x: Result<BoundedInt32<-100, 100>, _> = 50.try_into();
706 let y: i32 = x.unwrap().into();
707 assert_eq!(y, 50);
708
709 let x: Result<BoundedInt32<-100, 100>, _> = 1000.try_into();
710 assert!(x.is_err());
711 }
712
713 #[test]
714 fn into_bool() {
715 let zero: BoundedInt32<0, 1> = BoundedInt32::saturating_from(0);
716 let one: BoundedInt32<0, 1> = BoundedInt32::saturating_from(1);
717
718 let f: bool = zero.into();
719 let t: bool = one.into();
720 assert!(!f);
721 assert!(t);
722 }
723
724 #[test]
725 fn into_u8() {
726 let zero: BoundedInt32<0, 255> = BoundedInt32::saturating_from(0);
727 let one: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1);
728 let ninety: BoundedInt32<0, 255> = BoundedInt32::saturating_from(90);
729 let max: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1000);
730
731 let a: u8 = zero.into();
732 let b: u8 = one.into();
733 let c: u8 = ninety.into();
734 let d: u8 = max.into();
735
736 assert_eq!(a, 0);
737 assert_eq!(b, 1);
738 assert_eq!(c, 90);
739 assert_eq!(d, 255);
740 }
741
742 #[test]
743 fn into_u32() {
744 let zero: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(0);
745 let one: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1);
746 let ninety: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(90);
747 let max: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1000);
748
749 assert_eq!(u32::from(zero), 0);
750 assert_eq!(u32::from(one), 1);
751 assert_eq!(u32::from(ninety), 90);
752 assert_eq!(u32::from(max), 1000);
753
754 let zero: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(0);
755 let one: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1);
756 let ninety: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(90);
757 let max: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1000);
758
759 assert_eq!(u32::from(zero), 1);
760 assert_eq!(u32::from(one), 1);
761 assert_eq!(u32::from(ninety), 90);
762 assert_eq!(u32::from(max), 1000);
763 }
764
765 #[test]
766 fn try_into_usize() {
767 let b0: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(0);
768 let b100: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(100);
769 let bn5: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(-5);
770 assert_eq!(usize::try_from(b0), Ok(0_usize));
771 assert_eq!(usize::try_from(b100), Ok(100_usize));
772 assert_eq!(usize::try_from(bn5), Err(Error::Negative));
773 }
774
775 #[test]
776 fn percents() {
777 type Pct = Percentage<u8>;
778 let p = Pct::new(100);
779 assert_eq!(p.as_percent(), 100);
780 assert_approx_eq!(f64, p.as_fraction(), 1.0);
781
782 let p = Pct::new(0);
783 assert_eq!(p.as_percent(), 0);
784 assert_approx_eq!(f64, p.as_fraction(), 0.0);
785
786 let p = Pct::new(25);
787 assert_eq!(p.as_percent(), 25);
788 assert_eq!(p.clone(), p);
789 assert_approx_eq!(f64, p.as_fraction(), 0.25);
790
791 type BPct = Percentage<BoundedInt32<0, 100>>;
792 assert_eq!(BPct::try_from(99).unwrap().as_percent().get(), 99);
793 }
794
795 #[test]
796 fn milliseconds() {
797 type Msec = IntegerMilliseconds<i32>;
798
799 let ms = Msec::new(500);
800 let d: Result<Duration, _> = ms.try_into();
801 assert_eq!(d.unwrap(), Duration::from_millis(500));
802 assert_eq!(Duration::try_from(ms * 2).unwrap(), Duration::from_secs(1));
803
804 let ms = Msec::new(-100);
805 let d: Result<Duration, _> = ms.try_into();
806 assert!(d.is_err());
807
808 type BMSec = IntegerMilliseconds<BoundedInt32<0, 1000>>;
809 let half_sec = BMSec::try_from(500).unwrap();
810 assert_eq!(
811 Duration::try_from(half_sec).unwrap(),
812 Duration::from_millis(500)
813 );
814 assert!(BMSec::try_from(1001).is_err());
815 }
816
817 #[test]
818 fn seconds() {
819 type Sec = IntegerSeconds<i32>;
820
821 let ms = Sec::new(500);
822 let d: Result<Duration, _> = ms.try_into();
823 assert_eq!(d.unwrap(), Duration::from_secs(500));
824
825 let ms = Sec::new(-100);
826 let d: Result<Duration, _> = ms.try_into();
827 assert!(d.is_err());
828
829 type BSec = IntegerSeconds<BoundedInt32<0, 3600>>;
830 let half_hour = BSec::try_from(1800).unwrap();
831 assert_eq!(
832 Duration::try_from(half_hour).unwrap(),
833 Duration::from_secs(1800)
834 );
835 assert!(BSec::try_from(9999).is_err());
836 assert_eq!(half_hour.clone(), half_hour);
837 }
838
839 #[test]
840 fn minutes() {
841 type Min = IntegerMinutes<i32>;
842
843 let t = Min::new(500);
844 let d: Duration = t.try_into().unwrap();
845 assert_eq!(d, Duration::from_secs(500 * 60));
846
847 let t = Min::new(-100);
848 let d: Result<Duration, _> = t.try_into();
849 assert_eq!(d, Err(Error::Overflow));
850
851 let t = IntegerMinutes::<u64>::new(u64::MAX);
852 let d: Result<Duration, _> = t.try_into();
853 assert_eq!(d, Err(Error::Overflow));
854
855 type BMin = IntegerMinutes<BoundedInt32<10, 30>>;
856 assert_eq!(
857 BMin::new(17_i32.try_into().unwrap()),
858 BMin::try_from(17).unwrap()
859 );
860 }
861
862 #[test]
863 fn days() {
864 type Days = IntegerDays<i32>;
865
866 let t = Days::new(500);
867 let d: Duration = t.try_into().unwrap();
868 assert_eq!(d, Duration::from_secs(500 * 86400));
869
870 let t = Days::new(-100);
871 let d: Result<Duration, _> = t.try_into();
872 assert_eq!(d, Err(Error::Overflow));
873
874 let t = IntegerDays::<u64>::new(u64::MAX);
875 let d: Result<Duration, _> = t.try_into();
876 assert_eq!(d, Err(Error::Overflow));
877
878 type BDays = IntegerDays<BoundedInt32<10, 30>>;
879 assert_eq!(
880 BDays::new(17_i32.try_into().unwrap()),
881 BDays::try_from(17).unwrap()
882 );
883 }
884
885 #[test]
886 fn sendme() {
887 let smv = SendMeVersion::new(5);
888 assert_eq!(smv.get(), 5);
889 assert_eq!(smv.clone().get(), 5);
890 assert_eq!(smv, SendMeVersion::try_from(5).unwrap());
891 }
892}