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