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