1
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// @@ begin lint list maintained by maint/add_warning @@
4
#![cfg_attr(not(ci_arti_stable), allow(renamed_and_removed_lints))]
5
#![cfg_attr(not(ci_arti_nightly), allow(unknown_lints))]
6
#![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::unnecessary_wraps)]
34
#![warn(clippy::unseparated_literal_suffix)]
35
#![deny(clippy::unwrap_used)]
36
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
37
#![allow(clippy::uninlined_format_args)]
38
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
39
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
40
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
41
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
42

            
43
use derive_more::{Add, Display, Div, From, FromStr, Mul};
44

            
45
use std::time::Duration;
46
use thiserror::Error;
47

            
48
/// Conversion errors from converting a value into a [`BoundedInt32`].
49
16
#[derive(Debug, Clone, PartialEq, Eq, Error)]
50
#[non_exhaustive]
51
pub enum Error {
52
    /// A passed value was below the lower bound for the type.
53
    #[error("Value {0} was below the lower bound {1} for this type")]
54
    BelowLowerBound(i32, i32),
55
    /// A passed value was above the upper bound for the type.
56
    #[error("Value {0} was above the lower bound {1} for this type")]
57
    AboveUpperBound(i32, i32),
58
    /// Tried to convert a negative value to an unsigned type.
59
    #[error("Tried to convert a negative value to an unsigned type")]
60
    Negative,
61
    /// Tried to parse a value that was not representable as the
62
    /// underlying type.
63
    #[error("Value could not be represented as an i32")]
64
    Unrepresentable,
65
    /// We encountered some kind of integer overflow when converting a number.
66
    #[error("Integer overflow")]
67
    Overflow,
68
    /// Tried to instantiate an uninhabited type.
69
    #[error("No value is valid for this type")]
70
    Uninhabited,
71
}
72

            
73
/// A 32-bit signed integer with a restricted range.
74
///
75
/// This type holds an i32 value such that `LOWER` <= value <= `UPPER`
76
///
77
/// # Limitations
78
///
79
/// If you try to instantiate this type with LOWER > UPPER, you will
80
/// get an uninhabitable type.  It would be better if we could check that at
81
/// compile time, and prevent such types from being named.
82
//
83
// [TODO: If you need a Bounded* for some type other than i32, ask nickm:
84
// he has an implementation kicking around.]
85
3102
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
86
pub struct BoundedInt32<const LOWER: i32, const UPPER: i32> {
87
    /// Interior Value
88
    value: i32,
89
}
90

            
91
impl<const LOWER: i32, const UPPER: i32> BoundedInt32<LOWER, UPPER> {
92
    /// Lower bound
93
    pub const LOWER: i32 = LOWER;
94
    /// Upper bound
95
    pub const UPPER: i32 = UPPER;
96

            
97
    /// Private constructor function for this type.
98
367870
    fn unchecked_new(value: i32) -> Self {
99
367870
        assert!(LOWER <= UPPER); //The compiler optimizes this out, no run-time cost.
100

            
101
367870
        BoundedInt32 { value }
102
367870
    }
103

            
104
    /// Return the underlying i32 value.
105
    ///
106
    /// This value will always be between [`Self::LOWER`] and [`Self::UPPER`],
107
    /// inclusive.
108
15398
    pub fn get(&self) -> i32 {
109
15398
        self.value
110
15398
    }
111

            
112
    /// If `val` is within range, return a new `BoundedInt32` wrapping
113
    /// it; otherwise, clamp it to the upper or lower bound as
114
    /// appropriate.
115
5926
    pub fn saturating_new(val: i32) -> Self {
116
5926
        Self::unchecked_new(Self::clamp(val))
117
5926
    }
118

            
119
    /// If `val` is an acceptable value inside the range for this type,
120
    /// return a new [`BoundedInt32`].  Otherwise return an error.
121
361922
    pub fn checked_new(val: i32) -> Result<Self, Error> {
122
361922
        if val > UPPER {
123
10
            Err(Error::AboveUpperBound(val, UPPER))
124
361912
        } else if val < LOWER {
125
4
            Err(Error::BelowLowerBound(val, LOWER))
126
        } else {
127
361908
            Ok(BoundedInt32::unchecked_new(val))
128
        }
129
361922
    }
130

            
131
    /// This private function clamps an input to the acceptable range.
132
5964
    fn clamp(val: i32) -> i32 {
133
5964
        Ord::clamp(val, LOWER, UPPER)
134
5964
    }
135

            
136
    /// Convert from the underlying type, clamping to the upper or
137
    /// lower bound if needed.
138
    ///
139
    /// # Panics
140
    ///
141
    /// This function will panic if UPPER < LOWER.
142
38
    pub fn saturating_from(val: i32) -> Self {
143
38
        Self::unchecked_new(Self::clamp(val))
144
38
    }
145

            
146
    /// Convert from a string, clamping to the upper or lower bound if needed.
147
    ///
148
    /// # Limitations
149
    ///
150
    /// If the input is a number that cannot be represented as an i32,
151
    /// then we return an error instead of clamping it.
152
6
    pub fn saturating_from_str(s: &str) -> Result<Self, Error> {
153
6
        if UPPER < LOWER {
154
            // The compiler should optimize this block out at compile time.
155
2
            return Err(Error::Uninhabited);
156
4
        }
157
4
        let val: i32 = s.parse().map_err(|_| Error::Unrepresentable)?;
158
4
        Ok(Self::saturating_from(val))
159
6
    }
160
}
161

            
162
impl<const L: i32, const U: i32> std::fmt::Display for BoundedInt32<L, U> {
163
2
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164
2
        write!(f, "{}", self.value)
165
2
    }
166
}
167

            
168
impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for i32 {
169
10
    fn from(val: BoundedInt32<L, U>) -> i32 {
170
10
        val.value
171
10
    }
172
}
173

            
174
impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for f64 {
175
4285
    fn from(val: BoundedInt32<L, U>) -> f64 {
176
4285
        val.value.into()
177
4285
    }
178
}
179

            
180
impl<const L: i32, const H: i32> TryFrom<i32> for BoundedInt32<L, H> {
181
    type Error = Error;
182
329032
    fn try_from(val: i32) -> Result<Self, Self::Error> {
183
329032
        Self::checked_new(val)
184
329032
    }
185
}
186

            
187
impl<const L: i32, const H: i32> std::str::FromStr for BoundedInt32<L, H> {
188
    type Err = Error;
189
20
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
190
20
        Self::checked_new(s.parse().map_err(|_| Error::Unrepresentable)?)
191
20
    }
192
}
193

            
194
impl From<BoundedInt32<0, 1>> for bool {
195
384
    fn from(val: BoundedInt32<0, 1>) -> bool {
196
384
        val.value == 1
197
384
    }
198
}
199

            
200
impl From<BoundedInt32<0, 255>> for u8 {
201
8
    fn from(val: BoundedInt32<0, 255>) -> u8 {
202
8
        val.value as u8
203
8
    }
204
}
205

            
206
impl<const H: i32> From<BoundedInt32<0, H>> for u32 {
207
2772
    fn from(val: BoundedInt32<0, H>) -> u32 {
208
2772
        val.value as u32
209
2772
    }
210
}
211

            
212
impl<const H: i32> From<BoundedInt32<1, H>> for u32 {
213
3267
    fn from(val: BoundedInt32<1, H>) -> u32 {
214
3267
        val.value as u32
215
3267
    }
216
}
217

            
218
impl<const L: i32, const H: i32> TryFrom<BoundedInt32<L, H>> for u64 {
219
    type Error = Error;
220
8970
    fn try_from(val: BoundedInt32<L, H>) -> Result<Self, Self::Error> {
221
8970
        if val.value < 0 {
222
2
            Err(Error::Negative)
223
        } else {
224
8968
            Ok(val.value as u64)
225
        }
226
8970
    }
227
}
228

            
229
impl<const L: i32, const H: i32> TryFrom<BoundedInt32<L, H>> for usize {
230
    type Error = Error;
231
1576
    fn try_from(val: BoundedInt32<L, H>) -> Result<Self, Self::Error> {
232
1576
        if val.value < 0 {
233
2
            Err(Error::Negative)
234
        } else {
235
1574
            Ok(val.value as usize)
236
        }
237
1576
    }
238
}
239

            
240
/// A percentage value represented as a number.
241
///
242
/// This type wraps an underlying numeric type, and ensures that callers
243
/// are clear whether they want a _fraction_, or a _percentage_.
244
394
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
245
pub struct Percentage<T: Copy + Into<f64>> {
246
    /// The underlying percentage value.
247
    value: T,
248
}
249

            
250
impl<T: Copy + Into<f64>> Percentage<T> {
251
    /// Create a new `IntPercentage` from the underlying percentage.
252
42504
    pub fn new(value: T) -> Self {
253
42504
        Self { value }
254
42504
    }
255

            
256
    /// Return this value as a (possibly improper) fraction.
257
    ///
258
    /// ```
259
    /// use tor_units::Percentage;
260
    /// let pct_200 = Percentage::<u8>::new(200);
261
    /// let pct_100 = Percentage::<u8>::new(100);
262
    /// let pct_50 = Percentage::<u8>::new(50);
263
    ///
264
    /// assert_eq!(pct_200.as_fraction(), 2.0);
265
    /// assert_eq!(pct_100.as_fraction(), 1.0);
266
    /// assert_eq!(pct_50.as_fraction(), 0.5);
267
    /// // Note: don't actually compare f64 with ==.
268
    /// ```
269
4289
    pub fn as_fraction(self) -> f64 {
270
4289
        self.value.into() / 100.0
271
4289
    }
272

            
273
    /// Return this value as a percentage.
274
    ///
275
    /// ```
276
    /// use tor_units::Percentage;
277
    /// let pct_200 = Percentage::<u8>::new(200);
278
    /// let pct_100 = Percentage::<u8>::new(100);
279
    /// let pct_50 = Percentage::<u8>::new(50);
280
    ///
281
    /// assert_eq!(pct_200.as_percent(), 200);
282
    /// assert_eq!(pct_100.as_percent(), 100);
283
    /// assert_eq!(pct_50.as_percent(), 50);
284
    /// ```
285
44
    pub fn as_percent(self) -> T {
286
44
        self.value
287
44
    }
288
}
289

            
290
impl<const H: i32, const L: i32> TryFrom<i32> for Percentage<BoundedInt32<H, L>> {
291
    type Error = Error;
292
41876
    fn try_from(v: i32) -> Result<Self, Error> {
293
41876
        Ok(Percentage::new(v.try_into()?))
294
41876
    }
295
}
296

            
297
// TODO: There is a bunch of code duplication among these "IntegerTimeUnits"
298
// section.
299

            
300
#[derive(
301
7150
    Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
302
)]
303
/// This type represents an integer number of milliseconds.
304
///
305
/// The underlying type should usually implement `TryInto<u64>`.
306
pub struct IntegerMilliseconds<T> {
307
    /// Interior Value. Should implement `TryInto<u64>` to be useful.
308
    value: T,
309
}
310

            
311
impl<T> IntegerMilliseconds<T> {
312
    /// Public Constructor
313
45328
    pub fn new(value: T) -> Self {
314
45328
        IntegerMilliseconds { value }
315
45328
    }
316

            
317
    /// Deconstructor
318
    ///
319
    /// Use only in contexts where it's no longer possible to
320
    /// use the Rust type system to ensure secs vs ms vs us correctness.
321
3262
    pub fn as_millis(self) -> T {
322
3262
        self.value
323
3262
    }
324

            
325
    /// Map the inner value (useful for conversion)
326
    ///
327
    /// # Example
328
    ///
329
    /// ```
330
    /// use tor_units::{BoundedInt32, IntegerMilliseconds};
331
    ///
332
    /// let value: IntegerMilliseconds<i32> = 42.into();
333
    /// let value: IntegerMilliseconds<BoundedInt32<0,1000>>
334
    ///     = value.try_map(TryInto::try_into).unwrap();
335
    /// ```
336
2764
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMilliseconds<U>, E>
337
2764
    where
338
2764
        F: FnOnce(T) -> Result<U, E>,
339
2764
    {
340
2764
        Ok(IntegerMilliseconds::new(f(self.value)?))
341
2764
    }
342
}
343

            
344
impl<T: TryInto<u64>> TryFrom<IntegerMilliseconds<T>> for Duration {
345
    type Error = <T as TryInto<u64>>::Error;
346
28
    fn try_from(val: IntegerMilliseconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
347
28
        Ok(Self::from_millis(val.value.try_into()?))
348
28
    }
349
}
350

            
351
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMilliseconds<BoundedInt32<H, L>> {
352
    type Error = Error;
353
41878
    fn try_from(v: i32) -> Result<Self, Error> {
354
41878
        Ok(IntegerMilliseconds::new(v.try_into()?))
355
41878
    }
356
}
357

            
358
#[derive(
359
450
    Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
360
)]
361
/// This type represents an integer number of seconds.
362
///
363
/// The underlying type should usually implement `TryInto<u64>`.
364
pub struct IntegerSeconds<T> {
365
    /// Interior Value. Should implement `TryInto<u64>` to be useful.
366
    value: T,
367
}
368

            
369
impl<T> IntegerSeconds<T> {
370
    /// Public Constructor
371
47874
    pub fn new(value: T) -> Self {
372
47874
        IntegerSeconds { value }
373
47874
    }
374

            
375
    /// Deconstructor
376
    ///
377
    /// Use only in contexts where it's no longer possible to
378
    /// use the Rust type system to ensure secs vs ms vs us correctness.
379
    pub fn as_secs(self) -> T {
380
        self.value
381
    }
382

            
383
    /// Map the inner value (useful for conversion)
384
    ///
385
    /// ```
386
    /// use tor_units::{BoundedInt32, IntegerSeconds};
387
    ///
388
    /// let value: IntegerSeconds<i32> = 42.into();
389
    /// let value: IntegerSeconds<BoundedInt32<0,1000>>
390
    ///     = value.try_map(TryInto::try_into).unwrap();
391
    /// ```
392
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerSeconds<U>, E>
393
    where
394
        F: FnOnce(T) -> Result<U, E>,
395
    {
396
        Ok(IntegerSeconds::new(f(self.value)?))
397
    }
398
}
399

            
400
impl<T: TryInto<u64>> TryFrom<IntegerSeconds<T>> for Duration {
401
    type Error = <T as TryInto<u64>>::Error;
402
1566
    fn try_from(val: IntegerSeconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
403
1566
        Ok(Self::from_secs(val.value.try_into()?))
404
1566
    }
405
}
406

            
407
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerSeconds<BoundedInt32<H, L>> {
408
    type Error = Error;
409
47860
    fn try_from(v: i32) -> Result<Self, Error> {
410
47860
        Ok(IntegerSeconds::new(v.try_into()?))
411
47860
    }
412
}
413

            
414
9219
#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
415
/// This type represents an integer number of minutes.
416
///
417
/// The underlying type should usually implement `TryInto<u64>`.
418
pub struct IntegerMinutes<T> {
419
    /// Interior Value. Should Implement `TryInto<u64>` to be useful.
420
    value: T,
421
}
422

            
423
impl<T> IntegerMinutes<T> {
424
    /// Public Constructor
425
16837
    pub fn new(value: T) -> Self {
426
16837
        IntegerMinutes { value }
427
16837
    }
428

            
429
    /// Deconstructor
430
    ///
431
    /// Use only in contexts where it's no longer possible to
432
    /// use the Rust type system to ensure secs vs ms vs us correctness.
433
100878
    pub fn as_minutes(self) -> T {
434
100878
        self.value
435
100878
    }
436

            
437
    /// Map the inner value (useful for conversion)
438
    ///
439
    /// ```
440
    /// use tor_units::{BoundedInt32, IntegerMinutes};
441
    ///
442
    /// let value: IntegerMinutes<i32> = 42.into();
443
    /// let value: IntegerMinutes<BoundedInt32<0,1000>>
444
    ///     = value.try_map(TryInto::try_into).unwrap();
445
    /// ```
446
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMinutes<U>, E>
447
    where
448
        F: FnOnce(T) -> Result<U, E>,
449
    {
450
        Ok(IntegerMinutes::new(f(self.value)?))
451
    }
452
}
453

            
454
impl<T: TryInto<u64>> TryFrom<IntegerMinutes<T>> for Duration {
455
    type Error = Error;
456
6444
    fn try_from(val: IntegerMinutes<T>) -> Result<Self, Error> {
457
        /// Number of seconds in a single minute.
458
        const SECONDS_PER_MINUTE: u64 = 60;
459
6445
        let minutes: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
460
6442
        let seconds = minutes
461
6442
            .checked_mul(SECONDS_PER_MINUTE)
462
6442
            .ok_or(Error::Overflow)?;
463
6440
        Ok(Self::from_secs(seconds))
464
6444
    }
465
}
466

            
467
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMinutes<BoundedInt32<H, L>> {
468
    type Error = Error;
469
5984
    fn try_from(v: i32) -> Result<Self, Error> {
470
5984
        Ok(IntegerMinutes::new(v.try_into()?))
471
5984
    }
472
}
473

            
474
170
#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
475
/// This type represents an integer number of days.
476
///
477
/// The underlying type should usually implement `TryInto<u64>`.
478
pub struct IntegerDays<T> {
479
    /// Interior Value. Should Implement `TryInto<u64>` to be useful.
480
    value: T,
481
}
482

            
483
impl<T> IntegerDays<T> {
484
    /// Public Constructor
485
17962
    pub fn new(value: T) -> Self {
486
17962
        IntegerDays { value }
487
17962
    }
488

            
489
    /// Deconstructor
490
    ///
491
    /// Use only in contexts where it's no longer possible to
492
    /// use the Rust type system to ensure secs vs ms vs us correctness.
493
    pub fn as_days(self) -> T {
494
        self.value
495
    }
496

            
497
    /// Map the inner value (useful for conversion)
498
    ///
499
    /// ```
500
    /// use tor_units::{BoundedInt32, IntegerDays};
501
    ///
502
    /// let value: IntegerDays<i32> = 42.into();
503
    /// let value: IntegerDays<BoundedInt32<0,1000>>
504
    ///     = value.try_map(TryInto::try_into).unwrap();
505
    /// ```
506
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerDays<U>, E>
507
    where
508
        F: FnOnce(T) -> Result<U, E>,
509
    {
510
        Ok(IntegerDays::new(f(self.value)?))
511
    }
512
}
513

            
514
impl<T: TryInto<u64>> TryFrom<IntegerDays<T>> for Duration {
515
    type Error = Error;
516
954
    fn try_from(val: IntegerDays<T>) -> Result<Self, Error> {
517
        /// Number of seconds in a single day.
518
        const SECONDS_PER_DAY: u64 = 86400;
519
955
        let days: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
520
952
        let seconds = days.checked_mul(SECONDS_PER_DAY).ok_or(Error::Overflow)?;
521
950
        Ok(Self::from_secs(seconds))
522
954
    }
523
}
524

            
525
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerDays<BoundedInt32<H, L>> {
526
    type Error = Error;
527
17948
    fn try_from(v: i32) -> Result<Self, Error> {
528
17948
        Ok(IntegerDays::new(v.try_into()?))
529
17948
    }
530
}
531

            
532
/// A SendMe Version
533
///
534
/// DOCDOC: Explain why this needs to have its own type, or remove it.
535
114
#[derive(Clone, Copy, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
536
pub struct SendMeVersion(u8);
537

            
538
impl SendMeVersion {
539
    /// Public Constructor
540
14710
    pub fn new(value: u8) -> Self {
541
14710
        SendMeVersion(value)
542
14710
    }
543

            
544
    /// Helper
545
194
    pub fn get(&self) -> u8 {
546
194
        self.0
547
194
    }
548
}
549

            
550
impl TryFrom<i32> for SendMeVersion {
551
    type Error = Error;
552
14518
    fn try_from(v: i32) -> Result<Self, Error> {
553
14518
        let val_u8 = BoundedInt32::<0, 255>::checked_new(v)?;
554
14518
        Ok(SendMeVersion::new(val_u8.get() as u8))
555
14518
    }
556
}
557

            
558
#[cfg(test)]
559
mod tests {
560
    #![allow(clippy::unwrap_used)]
561
    use float_cmp::assert_approx_eq;
562

            
563
    use super::*;
564

            
565
    type TestFoo = BoundedInt32<1, 5>;
566
    type TestBar = BoundedInt32<-45, 17>;
567

            
568
    //make_parameter_type! {TestFoo(3,)}
569
    #[test]
570
    fn entire_range_parsed() {
571
        let x: TestFoo = "1".parse().unwrap();
572
        assert!(x.get() == 1);
573
        let x: TestFoo = "2".parse().unwrap();
574
        assert!(x.get() == 2);
575
        let x: TestFoo = "3".parse().unwrap();
576
        assert!(x.get() == 3);
577
        let x: TestFoo = "4".parse().unwrap();
578
        assert!(x.get() == 4);
579
        let x: TestFoo = "5".parse().unwrap();
580
        assert!(x.get() == 5);
581
    }
582

            
583
    #[test]
584
    fn saturating() {
585
        let x: TestFoo = TestFoo::saturating_new(1000);
586
        let x_val: i32 = x.into();
587
        assert!(x_val == TestFoo::UPPER);
588
        let x: TestFoo = TestFoo::saturating_new(0);
589
        let x_val: i32 = x.into();
590
        assert!(x_val == TestFoo::LOWER);
591
    }
592
    #[test]
593
    fn saturating_string() {
594
        let x: TestFoo = TestFoo::saturating_from_str("1000").unwrap();
595
        let x_val: i32 = x.into();
596
        assert!(x_val == TestFoo::UPPER);
597
        let x: TestFoo = TestFoo::saturating_from_str("0").unwrap();
598
        let x_val: i32 = x.into();
599
        assert!(x_val == TestFoo::LOWER);
600
    }
601

            
602
    #[test]
603
    #[should_panic]
604
    fn uninhabited_saturating_new() {
605
        // This value should be uncreatable.
606
        let _: BoundedInt32<10, 5> = BoundedInt32::saturating_new(7);
607
    }
608

            
609
    #[test]
610
    fn uninhabited_from_string() {
611
        let v: Result<BoundedInt32<10, 5>, Error> = BoundedInt32::saturating_from_str("7");
612
        assert!(matches!(v, Err(Error::Uninhabited)));
613
    }
614

            
615
    #[test]
616
    fn errors_correct() {
617
        let x: Result<TestBar, Error> = "1000".parse();
618
        assert!(x.unwrap_err() == Error::AboveUpperBound(1000, TestBar::UPPER));
619
        let x: Result<TestBar, Error> = "-1000".parse();
620
        assert!(x.unwrap_err() == Error::BelowLowerBound(-1000, TestBar::LOWER));
621
        let x: Result<TestBar, Error> = "xyz".parse();
622
        assert!(x.unwrap_err() == Error::Unrepresentable);
623
    }
624

            
625
    #[test]
626
    fn display() {
627
        let v = BoundedInt32::<99, 1000>::checked_new(345).unwrap();
628
        assert_eq!(v.to_string(), "345".to_string());
629
    }
630

            
631
    #[test]
632
    #[should_panic]
633
    fn checked_too_high() {
634
        let _: TestBar = "1000".parse().unwrap();
635
    }
636

            
637
    #[test]
638
    #[should_panic]
639
    fn checked_too_low() {
640
        let _: TestBar = "-46".parse().unwrap();
641
    }
642

            
643
    #[test]
644
    fn bounded_to_u64() {
645
        let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
646
        let u: u64 = b.try_into().unwrap();
647
        assert_eq!(u, 77);
648

            
649
        let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(-77).unwrap();
650
        let u: Result<u64, Error> = b.try_into();
651
        assert!(u.is_err());
652
    }
653

            
654
    #[test]
655
    fn bounded_to_f64() {
656
        let x: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
657
        let f: f64 = x.into();
658
        assert_approx_eq!(f64, f, 77.0);
659
    }
660

            
661
    #[test]
662
    fn bounded_from_i32() {
663
        let x: Result<BoundedInt32<-100, 100>, _> = 50.try_into();
664
        let y: i32 = x.unwrap().into();
665
        assert_eq!(y, 50);
666

            
667
        let x: Result<BoundedInt32<-100, 100>, _> = 1000.try_into();
668
        assert!(x.is_err());
669
    }
670

            
671
    #[test]
672
    fn into_bool() {
673
        let zero: BoundedInt32<0, 1> = BoundedInt32::saturating_from(0);
674
        let one: BoundedInt32<0, 1> = BoundedInt32::saturating_from(1);
675

            
676
        let f: bool = zero.into();
677
        let t: bool = one.into();
678
        assert!(!f);
679
        assert!(t);
680
    }
681

            
682
    #[test]
683
    fn into_u8() {
684
        let zero: BoundedInt32<0, 255> = BoundedInt32::saturating_from(0);
685
        let one: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1);
686
        let ninety: BoundedInt32<0, 255> = BoundedInt32::saturating_from(90);
687
        let max: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1000);
688

            
689
        let a: u8 = zero.into();
690
        let b: u8 = one.into();
691
        let c: u8 = ninety.into();
692
        let d: u8 = max.into();
693

            
694
        assert_eq!(a, 0);
695
        assert_eq!(b, 1);
696
        assert_eq!(c, 90);
697
        assert_eq!(d, 255);
698
    }
699

            
700
    #[test]
701
    fn into_u32() {
702
        let zero: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(0);
703
        let one: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1);
704
        let ninety: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(90);
705
        let max: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1000);
706

            
707
        assert_eq!(u32::from(zero), 0);
708
        assert_eq!(u32::from(one), 1);
709
        assert_eq!(u32::from(ninety), 90);
710
        assert_eq!(u32::from(max), 1000);
711

            
712
        let zero: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(0);
713
        let one: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1);
714
        let ninety: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(90);
715
        let max: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1000);
716

            
717
        assert_eq!(u32::from(zero), 1);
718
        assert_eq!(u32::from(one), 1);
719
        assert_eq!(u32::from(ninety), 90);
720
        assert_eq!(u32::from(max), 1000);
721
    }
722

            
723
    #[test]
724
    fn try_into_usize() {
725
        let b0: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(0);
726
        let b100: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(100);
727
        let bn5: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(-5);
728
        assert_eq!(usize::try_from(b0), Ok(0_usize));
729
        assert_eq!(usize::try_from(b100), Ok(100_usize));
730
        assert_eq!(usize::try_from(bn5), Err(Error::Negative));
731
    }
732

            
733
    #[test]
734
    fn percents() {
735
        type Pct = Percentage<u8>;
736
        let p = Pct::new(100);
737
        assert_eq!(p.as_percent(), 100);
738
        assert_approx_eq!(f64, p.as_fraction(), 1.0);
739

            
740
        let p = Pct::new(0);
741
        assert_eq!(p.as_percent(), 0);
742
        assert_approx_eq!(f64, p.as_fraction(), 0.0);
743

            
744
        let p = Pct::new(25);
745
        assert_eq!(p.as_percent(), 25);
746
        assert_eq!(p.clone(), p);
747
        assert_approx_eq!(f64, p.as_fraction(), 0.25);
748

            
749
        type BPct = Percentage<BoundedInt32<0, 100>>;
750
        assert_eq!(BPct::try_from(99).unwrap().as_percent().get(), 99);
751
    }
752

            
753
    #[test]
754
    fn milliseconds() {
755
        type Msec = IntegerMilliseconds<i32>;
756

            
757
        let ms = Msec::new(500);
758
        let d: Result<Duration, _> = ms.try_into();
759
        assert_eq!(d.unwrap(), Duration::from_millis(500));
760
        assert_eq!(Duration::try_from(ms * 2).unwrap(), Duration::from_secs(1));
761

            
762
        let ms = Msec::new(-100);
763
        let d: Result<Duration, _> = ms.try_into();
764
        assert!(d.is_err());
765

            
766
        type BMSec = IntegerMilliseconds<BoundedInt32<0, 1000>>;
767
        let half_sec = BMSec::try_from(500).unwrap();
768
        assert_eq!(
769
            Duration::try_from(half_sec).unwrap(),
770
            Duration::from_millis(500)
771
        );
772
        assert!(BMSec::try_from(1001).is_err());
773
    }
774

            
775
    #[test]
776
    fn seconds() {
777
        type Sec = IntegerSeconds<i32>;
778

            
779
        let ms = Sec::new(500);
780
        let d: Result<Duration, _> = ms.try_into();
781
        assert_eq!(d.unwrap(), Duration::from_secs(500));
782

            
783
        let ms = Sec::new(-100);
784
        let d: Result<Duration, _> = ms.try_into();
785
        assert!(d.is_err());
786

            
787
        type BSec = IntegerSeconds<BoundedInt32<0, 3600>>;
788
        let half_hour = BSec::try_from(1800).unwrap();
789
        assert_eq!(
790
            Duration::try_from(half_hour).unwrap(),
791
            Duration::from_secs(1800)
792
        );
793
        assert!(BSec::try_from(9999).is_err());
794
        assert_eq!(half_hour.clone(), half_hour);
795
    }
796

            
797
    #[test]
798
    fn minutes() {
799
        type Min = IntegerMinutes<i32>;
800

            
801
        let t = Min::new(500);
802
        let d: Duration = t.try_into().unwrap();
803
        assert_eq!(d, Duration::from_secs(500 * 60));
804

            
805
        let t = Min::new(-100);
806
        let d: Result<Duration, _> = t.try_into();
807
        assert_eq!(d, Err(Error::Overflow));
808

            
809
        let t = IntegerMinutes::<u64>::new(u64::MAX);
810
        let d: Result<Duration, _> = t.try_into();
811
        assert_eq!(d, Err(Error::Overflow));
812

            
813
        type BMin = IntegerMinutes<BoundedInt32<10, 30>>;
814
        assert_eq!(
815
            BMin::new(17_i32.try_into().unwrap()),
816
            BMin::try_from(17).unwrap()
817
        );
818
    }
819

            
820
    #[test]
821
    fn days() {
822
        type Days = IntegerDays<i32>;
823

            
824
        let t = Days::new(500);
825
        let d: Duration = t.try_into().unwrap();
826
        assert_eq!(d, Duration::from_secs(500 * 86400));
827

            
828
        let t = Days::new(-100);
829
        let d: Result<Duration, _> = t.try_into();
830
        assert_eq!(d, Err(Error::Overflow));
831

            
832
        let t = IntegerDays::<u64>::new(u64::MAX);
833
        let d: Result<Duration, _> = t.try_into();
834
        assert_eq!(d, Err(Error::Overflow));
835

            
836
        type BDays = IntegerDays<BoundedInt32<10, 30>>;
837
        assert_eq!(
838
            BDays::new(17_i32.try_into().unwrap()),
839
            BDays::try_from(17).unwrap()
840
        );
841
    }
842

            
843
    #[test]
844
    fn sendme() {
845
        let smv = SendMeVersion::new(5);
846
        assert_eq!(smv.get(), 5);
847
        assert_eq!(smv.clone().get(), 5);
848
        assert_eq!(smv, SendMeVersion::try_from(5).unwrap());
849
    }
850
}