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