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