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
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
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::unchecked_duration_subtraction)]
34
#![deny(clippy::unnecessary_wraps)]
35
#![warn(clippy::unseparated_literal_suffix)]
36
#![deny(clippy::unwrap_used)]
37
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
38
#![allow(clippy::uninlined_format_args)]
39
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
40
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
41
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
42
#![allow(clippy::needless_lifetimes)] // See arti#1765
43
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
44

            
45
use derive_more::{Add, Display, Div, From, FromStr, Mul};
46

            
47
use std::time::Duration;
48
use thiserror::Error;
49

            
50
#[cfg(feature = "memquota-memcost")]
51
use {derive_deftly::Deftly, tor_memquota::derive_deftly_template_HasMemoryCost};
52

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

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

            
101
impl<const LOWER: i32, const UPPER: i32> BoundedInt32<LOWER, UPPER> {
102
    /// Lower bound
103
    pub const LOWER: i32 = LOWER;
104
    /// Upper bound
105
    pub const UPPER: i32 = UPPER;
106

            
107
    /// Private constructor function for this type.
108
696906
    fn unchecked_new(value: i32) -> Self {
109
696906
        assert!(LOWER <= UPPER); //The compiler optimizes this out, no run-time cost.
110

            
111
696906
        BoundedInt32 { value }
112
696906
    }
113

            
114
    /// Return the underlying i32 value.
115
    ///
116
    /// This value will always be between [`Self::LOWER`] and [`Self::UPPER`],
117
    /// inclusive.
118
28366
    pub fn get(&self) -> i32 {
119
28366
        self.value
120
28366
    }
121

            
122
    /// If `val` is within range, return a new `BoundedInt32` wrapping
123
    /// it; otherwise, clamp it to the upper or lower bound as
124
    /// appropriate.
125
10234
    pub fn saturating_new(val: i32) -> Self {
126
10234
        Self::unchecked_new(Self::clamp(val))
127
10234
    }
128

            
129
    /// If `val` is an acceptable value inside the range for this type,
130
    /// return a new [`BoundedInt32`].  Otherwise return an error.
131
686650
    pub fn checked_new(val: i32) -> Result<Self, Error> {
132
686650
        if val > UPPER {
133
10
            Err(Error::AboveUpperBound(val, UPPER))
134
686640
        } else if val < LOWER {
135
4
            Err(Error::BelowLowerBound(val, LOWER))
136
        } else {
137
686636
            Ok(BoundedInt32::unchecked_new(val))
138
        }
139
686650
    }
140

            
141
    /// This private function clamps an input to the acceptable range.
142
10272
    fn clamp(val: i32) -> i32 {
143
10272
        Ord::clamp(val, LOWER, UPPER)
144
10272
    }
145

            
146
    /// Convert from the underlying type, clamping to the upper or
147
    /// lower bound if needed.
148
    ///
149
    /// # Panics
150
    ///
151
    /// This function will panic if UPPER < LOWER.
152
38
    pub fn saturating_from(val: i32) -> Self {
153
38
        Self::unchecked_new(Self::clamp(val))
154
38
    }
155

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

            
172
impl<const L: i32, const U: i32> std::fmt::Display for BoundedInt32<L, U> {
173
2
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174
2
        write!(f, "{}", self.value)
175
2
    }
176
}
177

            
178
impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for i32 {
179
10
    fn from(val: BoundedInt32<L, U>) -> i32 {
180
10
        val.value
181
10
    }
182
}
183

            
184
impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for f64 {
185
9796
    fn from(val: BoundedInt32<L, U>) -> f64 {
186
9796
        val.value.into()
187
9796
    }
188
}
189

            
190
impl<const L: i32, const H: i32> TryFrom<i32> for BoundedInt32<L, H> {
191
    type Error = Error;
192
638968
    fn try_from(val: i32) -> Result<Self, Self::Error> {
193
638968
        Self::checked_new(val)
194
638968
    }
195
}
196

            
197
impl<const L: i32, const H: i32> std::str::FromStr for BoundedInt32<L, H> {
198
    type Err = Error;
199
20
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
200
20
        Self::checked_new(s.parse().map_err(|_| Error::Unrepresentable)?)
201
20
    }
202
}
203

            
204
impl From<BoundedInt32<0, 1>> for bool {
205
414
    fn from(val: BoundedInt32<0, 1>) -> bool {
206
414
        val.value == 1
207
414
    }
208
}
209

            
210
impl From<BoundedInt32<0, 255>> for u8 {
211
8
    fn from(val: BoundedInt32<0, 255>) -> u8 {
212
8
        val.value as u8
213
8
    }
214
}
215

            
216
impl<const H: i32> From<BoundedInt32<0, H>> for u32 {
217
3492
    fn from(val: BoundedInt32<0, H>) -> u32 {
218
3492
        val.value as u32
219
3492
    }
220
}
221

            
222
impl<const H: i32> From<BoundedInt32<1, H>> for u32 {
223
6648
    fn from(val: BoundedInt32<1, H>) -> u32 {
224
6648
        val.value as u32
225
6648
    }
226
}
227

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

            
239
impl<const L: i32, const H: i32> TryFrom<BoundedInt32<L, H>> for usize {
240
    type Error = Error;
241
9340
    fn try_from(val: BoundedInt32<L, H>) -> Result<Self, Self::Error> {
242
9340
        if val.value < 0 {
243
2
            Err(Error::Negative)
244
        } else {
245
9338
            Ok(val.value as usize)
246
        }
247
9340
    }
248
}
249

            
250
/// A percentage value represented as a number.
251
///
252
/// This type wraps an underlying numeric type, and ensures that callers
253
/// are clear whether they want a _fraction_, or a _percentage_.
254
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
255
pub struct Percentage<T: Copy + Into<f64>> {
256
    /// The underlying percentage value.
257
    value: T,
258
}
259

            
260
impl<T: Copy + Into<f64>> Percentage<T> {
261
    /// Create a new `IntPercentage` from the underlying percentage.
262
71672
    pub fn new(value: T) -> Self {
263
71672
        Self { value }
264
71672
    }
265

            
266
    /// Return this value as a (possibly improper) fraction.
267
    ///
268
    /// ```
269
    /// use tor_units::Percentage;
270
    /// let pct_200 = Percentage::<u8>::new(200);
271
    /// let pct_100 = Percentage::<u8>::new(100);
272
    /// let pct_50 = Percentage::<u8>::new(50);
273
    ///
274
    /// assert_eq!(pct_200.as_fraction(), 2.0);
275
    /// assert_eq!(pct_100.as_fraction(), 1.0);
276
    /// assert_eq!(pct_50.as_fraction(), 0.5);
277
    /// // Note: don't actually compare f64 with ==.
278
    /// ```
279
9800
    pub fn as_fraction(self) -> f64 {
280
9800
        self.value.into() / 100.0
281
9800
    }
282

            
283
    /// Return this value as a percentage.
284
    ///
285
    /// ```
286
    /// use tor_units::Percentage;
287
    /// let pct_200 = Percentage::<u8>::new(200);
288
    /// let pct_100 = Percentage::<u8>::new(100);
289
    /// let pct_50 = Percentage::<u8>::new(50);
290
    ///
291
    /// assert_eq!(pct_200.as_percent(), 200);
292
    /// assert_eq!(pct_100.as_percent(), 100);
293
    /// assert_eq!(pct_50.as_percent(), 50);
294
    /// ```
295
44
    pub fn as_percent(self) -> T {
296
44
        self.value
297
44
    }
298
}
299

            
300
impl<const H: i32, const L: i32> TryFrom<i32> for Percentage<BoundedInt32<H, L>> {
301
    type Error = Error;
302
70996
    fn try_from(v: i32) -> Result<Self, Error> {
303
70996
        Ok(Percentage::new(v.try_into()?))
304
70996
    }
305
}
306

            
307
// TODO: There is a bunch of code duplication among these "IntegerTimeUnits"
308
// section.
309

            
310
#[derive(
311
    Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
312
)]
313
/// This type represents an integer number of milliseconds.
314
///
315
/// The underlying type should usually implement `TryInto<u64>`.
316
pub struct IntegerMilliseconds<T> {
317
    /// Interior Value. Should implement `TryInto<u64>` to be useful.
318
    value: T,
319
}
320

            
321
impl<T> IntegerMilliseconds<T> {
322
    /// Public Constructor
323
75222
    pub fn new(value: T) -> Self {
324
75222
        IntegerMilliseconds { value }
325
75222
    }
326

            
327
    /// Deconstructor
328
    ///
329
    /// Use only in contexts where it's no longer possible to
330
    /// use the Rust type system to ensure secs vs ms vs us correctness.
331
3748
    pub fn as_millis(self) -> T {
332
3748
        self.value
333
3748
    }
334

            
335
    /// Map the inner value (useful for conversion)
336
    ///
337
    /// # Example
338
    ///
339
    /// ```
340
    /// use tor_units::{BoundedInt32, IntegerMilliseconds};
341
    ///
342
    /// let value: IntegerMilliseconds<i32> = 42.into();
343
    /// let value: IntegerMilliseconds<BoundedInt32<0,1000>>
344
    ///     = value.try_map(TryInto::try_into).unwrap();
345
    /// ```
346
3484
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMilliseconds<U>, E>
347
3484
    where
348
3484
        F: FnOnce(T) -> Result<U, E>,
349
3484
    {
350
3484
        Ok(IntegerMilliseconds::new(f(self.value)?))
351
3484
    }
352
}
353

            
354
impl<T: TryInto<u64>> TryFrom<IntegerMilliseconds<T>> for Duration {
355
    type Error = <T as TryInto<u64>>::Error;
356
28
    fn try_from(val: IntegerMilliseconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
357
28
        Ok(Self::from_millis(val.value.try_into()?))
358
28
    }
359
}
360

            
361
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMilliseconds<BoundedInt32<H, L>> {
362
    type Error = Error;
363
70998
    fn try_from(v: i32) -> Result<Self, Error> {
364
70998
        Ok(IntegerMilliseconds::new(v.try_into()?))
365
70998
    }
366
}
367

            
368
#[derive(
369
    Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
370
)]
371
/// This type represents an integer number of seconds.
372
///
373
/// The underlying type should usually implement `TryInto<u64>`.
374
pub struct IntegerSeconds<T> {
375
    /// Interior Value. Should implement `TryInto<u64>` to be useful.
376
    value: T,
377
}
378

            
379
impl<T> IntegerSeconds<T> {
380
    /// Public Constructor
381
121722
    pub fn new(value: T) -> Self {
382
121722
        IntegerSeconds { value }
383
121722
    }
384

            
385
    /// Deconstructor
386
    ///
387
    /// Use only in contexts where it's no longer possible to
388
    /// use the Rust type system to ensure secs vs ms vs us correctness.
389
    pub fn as_secs(self) -> T {
390
        self.value
391
    }
392

            
393
    /// Map the inner value (useful for conversion)
394
    ///
395
    /// ```
396
    /// use tor_units::{BoundedInt32, IntegerSeconds};
397
    ///
398
    /// let value: IntegerSeconds<i32> = 42.into();
399
    /// let value: IntegerSeconds<BoundedInt32<0,1000>>
400
    ///     = value.try_map(TryInto::try_into).unwrap();
401
    /// ```
402
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerSeconds<U>, E>
403
    where
404
        F: FnOnce(T) -> Result<U, E>,
405
    {
406
        Ok(IntegerSeconds::new(f(self.value)?))
407
    }
408
}
409

            
410
impl<T: TryInto<u64>> TryFrom<IntegerSeconds<T>> for Duration {
411
    type Error = <T as TryInto<u64>>::Error;
412
12184
    fn try_from(val: IntegerSeconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
413
12184
        Ok(Self::from_secs(val.value.try_into()?))
414
12184
    }
415
}
416

            
417
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerSeconds<BoundedInt32<H, L>> {
418
    type Error = Error;
419
121708
    fn try_from(v: i32) -> Result<Self, Error> {
420
121708
        Ok(IntegerSeconds::new(v.try_into()?))
421
121708
    }
422
}
423

            
424
#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
425
/// This type represents an integer number of minutes.
426
///
427
/// The underlying type should usually implement `TryInto<u64>`.
428
pub struct IntegerMinutes<T> {
429
    /// Interior Value. Should Implement `TryInto<u64>` to be useful.
430
    value: T,
431
}
432

            
433
impl<T> IntegerMinutes<T> {
434
    /// Public Constructor
435
30149
    pub fn new(value: T) -> Self {
436
30149
        IntegerMinutes { value }
437
30149
    }
438

            
439
    /// Deconstructor
440
    ///
441
    /// Use only in contexts where it's no longer possible to
442
    /// use the Rust type system to ensure secs vs ms vs us correctness.
443
188186
    pub fn as_minutes(self) -> T {
444
188186
        self.value
445
188186
    }
446

            
447
    /// Map the inner value (useful for conversion)
448
    ///
449
    /// ```
450
    /// use tor_units::{BoundedInt32, IntegerMinutes};
451
    ///
452
    /// let value: IntegerMinutes<i32> = 42.into();
453
    /// let value: IntegerMinutes<BoundedInt32<0,1000>>
454
    ///     = value.try_map(TryInto::try_into).unwrap();
455
    /// ```
456
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMinutes<U>, E>
457
    where
458
        F: FnOnce(T) -> Result<U, E>,
459
    {
460
        Ok(IntegerMinutes::new(f(self.value)?))
461
    }
462
}
463

            
464
impl<T: TryInto<u64>> TryFrom<IntegerMinutes<T>> for Duration {
465
    type Error = Error;
466
13200
    fn try_from(val: IntegerMinutes<T>) -> Result<Self, Error> {
467
        /// Number of seconds in a single minute.
468
        const SECONDS_PER_MINUTE: u64 = 60;
469
13201
        let minutes: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
470
13198
        let seconds = minutes
471
13198
            .checked_mul(SECONDS_PER_MINUTE)
472
13198
            .ok_or(Error::Overflow)?;
473
13196
        Ok(Self::from_secs(seconds))
474
13200
    }
475
}
476

            
477
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMinutes<BoundedInt32<H, L>> {
478
    type Error = Error;
479
10144
    fn try_from(v: i32) -> Result<Self, Error> {
480
10144
        Ok(IntegerMinutes::new(v.try_into()?))
481
10144
    }
482
}
483

            
484
#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
485
/// This type represents an integer number of days.
486
///
487
/// The underlying type should usually implement `TryInto<u64>`.
488
pub struct IntegerDays<T> {
489
    /// Interior Value. Should Implement `TryInto<u64>` to be useful.
490
    value: T,
491
}
492

            
493
impl<T> IntegerDays<T> {
494
    /// Public Constructor
495
30442
    pub fn new(value: T) -> Self {
496
30442
        IntegerDays { value }
497
30442
    }
498

            
499
    /// Deconstructor
500
    ///
501
    /// Use only in contexts where it's no longer possible to
502
    /// use the Rust type system to ensure secs vs ms vs us correctness.
503
    pub fn as_days(self) -> T {
504
        self.value
505
    }
506

            
507
    /// Map the inner value (useful for conversion)
508
    ///
509
    /// ```
510
    /// use tor_units::{BoundedInt32, IntegerDays};
511
    ///
512
    /// let value: IntegerDays<i32> = 42.into();
513
    /// let value: IntegerDays<BoundedInt32<0,1000>>
514
    ///     = value.try_map(TryInto::try_into).unwrap();
515
    /// ```
516
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerDays<U>, E>
517
    where
518
        F: FnOnce(T) -> Result<U, E>,
519
    {
520
        Ok(IntegerDays::new(f(self.value)?))
521
    }
522
}
523

            
524
impl<T: TryInto<u64>> TryFrom<IntegerDays<T>> for Duration {
525
    type Error = Error;
526
3078
    fn try_from(val: IntegerDays<T>) -> Result<Self, Error> {
527
        /// Number of seconds in a single day.
528
        const SECONDS_PER_DAY: u64 = 86400;
529
3079
        let days: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
530
3076
        let seconds = days.checked_mul(SECONDS_PER_DAY).ok_or(Error::Overflow)?;
531
3074
        Ok(Self::from_secs(seconds))
532
3078
    }
533
}
534

            
535
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerDays<BoundedInt32<H, L>> {
536
    type Error = Error;
537
30428
    fn try_from(v: i32) -> Result<Self, Error> {
538
30428
        Ok(IntegerDays::new(v.try_into()?))
539
30428
    }
540
}
541

            
542
/// A SendMe Version
543
///
544
/// DOCDOC: Explain why this needs to have its own type, or remove it.
545
#[derive(Clone, Copy, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
546
pub struct SendMeVersion(u8);
547

            
548
impl SendMeVersion {
549
    /// Public Constructor
550
23415
    pub fn new(value: u8) -> Self {
551
23415
        SendMeVersion(value)
552
23415
    }
553

            
554
    /// Helper
555
209
    pub fn get(&self) -> u8 {
556
209
        self.0
557
209
    }
558
}
559

            
560
impl TryFrom<i32> for SendMeVersion {
561
    type Error = Error;
562
23208
    fn try_from(v: i32) -> Result<Self, Error> {
563
23208
        let val_u8 = BoundedInt32::<0, 255>::checked_new(v)?;
564
23208
        Ok(SendMeVersion::new(val_u8.get() as u8))
565
23208
    }
566
}
567

            
568
#[cfg(test)]
569
mod tests {
570
    #![allow(clippy::unwrap_used)]
571
    use float_cmp::assert_approx_eq;
572

            
573
    use super::*;
574

            
575
    type TestFoo = BoundedInt32<1, 5>;
576
    type TestBar = BoundedInt32<-45, 17>;
577

            
578
    //make_parameter_type! {TestFoo(3,)}
579
    #[test]
580
    fn entire_range_parsed() {
581
        let x: TestFoo = "1".parse().unwrap();
582
        assert!(x.get() == 1);
583
        let x: TestFoo = "2".parse().unwrap();
584
        assert!(x.get() == 2);
585
        let x: TestFoo = "3".parse().unwrap();
586
        assert!(x.get() == 3);
587
        let x: TestFoo = "4".parse().unwrap();
588
        assert!(x.get() == 4);
589
        let x: TestFoo = "5".parse().unwrap();
590
        assert!(x.get() == 5);
591
    }
592

            
593
    #[test]
594
    fn saturating() {
595
        let x: TestFoo = TestFoo::saturating_new(1000);
596
        let x_val: i32 = x.into();
597
        assert!(x_val == TestFoo::UPPER);
598
        let x: TestFoo = TestFoo::saturating_new(0);
599
        let x_val: i32 = x.into();
600
        assert!(x_val == TestFoo::LOWER);
601
    }
602
    #[test]
603
    fn saturating_string() {
604
        let x: TestFoo = TestFoo::saturating_from_str("1000").unwrap();
605
        let x_val: i32 = x.into();
606
        assert!(x_val == TestFoo::UPPER);
607
        let x: TestFoo = TestFoo::saturating_from_str("0").unwrap();
608
        let x_val: i32 = x.into();
609
        assert!(x_val == TestFoo::LOWER);
610
    }
611

            
612
    #[test]
613
    #[should_panic]
614
    fn uninhabited_saturating_new() {
615
        // This value should be uncreatable.
616
        let _: BoundedInt32<10, 5> = BoundedInt32::saturating_new(7);
617
    }
618

            
619
    #[test]
620
    fn uninhabited_from_string() {
621
        let v: Result<BoundedInt32<10, 5>, Error> = BoundedInt32::saturating_from_str("7");
622
        assert!(matches!(v, Err(Error::Uninhabited)));
623
    }
624

            
625
    #[test]
626
    fn errors_correct() {
627
        let x: Result<TestBar, Error> = "1000".parse();
628
        assert!(x.unwrap_err() == Error::AboveUpperBound(1000, TestBar::UPPER));
629
        let x: Result<TestBar, Error> = "-1000".parse();
630
        assert!(x.unwrap_err() == Error::BelowLowerBound(-1000, TestBar::LOWER));
631
        let x: Result<TestBar, Error> = "xyz".parse();
632
        assert!(x.unwrap_err() == Error::Unrepresentable);
633
    }
634

            
635
    #[test]
636
    fn display() {
637
        let v = BoundedInt32::<99, 1000>::checked_new(345).unwrap();
638
        assert_eq!(v.to_string(), "345".to_string());
639
    }
640

            
641
    #[test]
642
    #[should_panic]
643
    fn checked_too_high() {
644
        let _: TestBar = "1000".parse().unwrap();
645
    }
646

            
647
    #[test]
648
    #[should_panic]
649
    fn checked_too_low() {
650
        let _: TestBar = "-46".parse().unwrap();
651
    }
652

            
653
    #[test]
654
    fn bounded_to_u64() {
655
        let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
656
        let u: u64 = b.try_into().unwrap();
657
        assert_eq!(u, 77);
658

            
659
        let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(-77).unwrap();
660
        let u: Result<u64, Error> = b.try_into();
661
        assert!(u.is_err());
662
    }
663

            
664
    #[test]
665
    fn bounded_to_f64() {
666
        let x: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
667
        let f: f64 = x.into();
668
        assert_approx_eq!(f64, f, 77.0);
669
    }
670

            
671
    #[test]
672
    fn bounded_from_i32() {
673
        let x: Result<BoundedInt32<-100, 100>, _> = 50.try_into();
674
        let y: i32 = x.unwrap().into();
675
        assert_eq!(y, 50);
676

            
677
        let x: Result<BoundedInt32<-100, 100>, _> = 1000.try_into();
678
        assert!(x.is_err());
679
    }
680

            
681
    #[test]
682
    fn into_bool() {
683
        let zero: BoundedInt32<0, 1> = BoundedInt32::saturating_from(0);
684
        let one: BoundedInt32<0, 1> = BoundedInt32::saturating_from(1);
685

            
686
        let f: bool = zero.into();
687
        let t: bool = one.into();
688
        assert!(!f);
689
        assert!(t);
690
    }
691

            
692
    #[test]
693
    fn into_u8() {
694
        let zero: BoundedInt32<0, 255> = BoundedInt32::saturating_from(0);
695
        let one: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1);
696
        let ninety: BoundedInt32<0, 255> = BoundedInt32::saturating_from(90);
697
        let max: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1000);
698

            
699
        let a: u8 = zero.into();
700
        let b: u8 = one.into();
701
        let c: u8 = ninety.into();
702
        let d: u8 = max.into();
703

            
704
        assert_eq!(a, 0);
705
        assert_eq!(b, 1);
706
        assert_eq!(c, 90);
707
        assert_eq!(d, 255);
708
    }
709

            
710
    #[test]
711
    fn into_u32() {
712
        let zero: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(0);
713
        let one: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1);
714
        let ninety: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(90);
715
        let max: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1000);
716

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

            
722
        let zero: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(0);
723
        let one: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1);
724
        let ninety: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(90);
725
        let max: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1000);
726

            
727
        assert_eq!(u32::from(zero), 1);
728
        assert_eq!(u32::from(one), 1);
729
        assert_eq!(u32::from(ninety), 90);
730
        assert_eq!(u32::from(max), 1000);
731
    }
732

            
733
    #[test]
734
    fn try_into_usize() {
735
        let b0: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(0);
736
        let b100: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(100);
737
        let bn5: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(-5);
738
        assert_eq!(usize::try_from(b0), Ok(0_usize));
739
        assert_eq!(usize::try_from(b100), Ok(100_usize));
740
        assert_eq!(usize::try_from(bn5), Err(Error::Negative));
741
    }
742

            
743
    #[test]
744
    fn percents() {
745
        type Pct = Percentage<u8>;
746
        let p = Pct::new(100);
747
        assert_eq!(p.as_percent(), 100);
748
        assert_approx_eq!(f64, p.as_fraction(), 1.0);
749

            
750
        let p = Pct::new(0);
751
        assert_eq!(p.as_percent(), 0);
752
        assert_approx_eq!(f64, p.as_fraction(), 0.0);
753

            
754
        let p = Pct::new(25);
755
        assert_eq!(p.as_percent(), 25);
756
        assert_eq!(p.clone(), p);
757
        assert_approx_eq!(f64, p.as_fraction(), 0.25);
758

            
759
        type BPct = Percentage<BoundedInt32<0, 100>>;
760
        assert_eq!(BPct::try_from(99).unwrap().as_percent().get(), 99);
761
    }
762

            
763
    #[test]
764
    fn milliseconds() {
765
        type Msec = IntegerMilliseconds<i32>;
766

            
767
        let ms = Msec::new(500);
768
        let d: Result<Duration, _> = ms.try_into();
769
        assert_eq!(d.unwrap(), Duration::from_millis(500));
770
        assert_eq!(Duration::try_from(ms * 2).unwrap(), Duration::from_secs(1));
771

            
772
        let ms = Msec::new(-100);
773
        let d: Result<Duration, _> = ms.try_into();
774
        assert!(d.is_err());
775

            
776
        type BMSec = IntegerMilliseconds<BoundedInt32<0, 1000>>;
777
        let half_sec = BMSec::try_from(500).unwrap();
778
        assert_eq!(
779
            Duration::try_from(half_sec).unwrap(),
780
            Duration::from_millis(500)
781
        );
782
        assert!(BMSec::try_from(1001).is_err());
783
    }
784

            
785
    #[test]
786
    fn seconds() {
787
        type Sec = IntegerSeconds<i32>;
788

            
789
        let ms = Sec::new(500);
790
        let d: Result<Duration, _> = ms.try_into();
791
        assert_eq!(d.unwrap(), Duration::from_secs(500));
792

            
793
        let ms = Sec::new(-100);
794
        let d: Result<Duration, _> = ms.try_into();
795
        assert!(d.is_err());
796

            
797
        type BSec = IntegerSeconds<BoundedInt32<0, 3600>>;
798
        let half_hour = BSec::try_from(1800).unwrap();
799
        assert_eq!(
800
            Duration::try_from(half_hour).unwrap(),
801
            Duration::from_secs(1800)
802
        );
803
        assert!(BSec::try_from(9999).is_err());
804
        assert_eq!(half_hour.clone(), half_hour);
805
    }
806

            
807
    #[test]
808
    fn minutes() {
809
        type Min = IntegerMinutes<i32>;
810

            
811
        let t = Min::new(500);
812
        let d: Duration = t.try_into().unwrap();
813
        assert_eq!(d, Duration::from_secs(500 * 60));
814

            
815
        let t = Min::new(-100);
816
        let d: Result<Duration, _> = t.try_into();
817
        assert_eq!(d, Err(Error::Overflow));
818

            
819
        let t = IntegerMinutes::<u64>::new(u64::MAX);
820
        let d: Result<Duration, _> = t.try_into();
821
        assert_eq!(d, Err(Error::Overflow));
822

            
823
        type BMin = IntegerMinutes<BoundedInt32<10, 30>>;
824
        assert_eq!(
825
            BMin::new(17_i32.try_into().unwrap()),
826
            BMin::try_from(17).unwrap()
827
        );
828
    }
829

            
830
    #[test]
831
    fn days() {
832
        type Days = IntegerDays<i32>;
833

            
834
        let t = Days::new(500);
835
        let d: Duration = t.try_into().unwrap();
836
        assert_eq!(d, Duration::from_secs(500 * 86400));
837

            
838
        let t = Days::new(-100);
839
        let d: Result<Duration, _> = t.try_into();
840
        assert_eq!(d, Err(Error::Overflow));
841

            
842
        let t = IntegerDays::<u64>::new(u64::MAX);
843
        let d: Result<Duration, _> = t.try_into();
844
        assert_eq!(d, Err(Error::Overflow));
845

            
846
        type BDays = IntegerDays<BoundedInt32<10, 30>>;
847
        assert_eq!(
848
            BDays::new(17_i32.try_into().unwrap()),
849
            BDays::try_from(17).unwrap()
850
        );
851
    }
852

            
853
    #[test]
854
    fn sendme() {
855
        let smv = SendMeVersion::new(5);
856
        assert_eq!(smv.get(), 5);
857
        assert_eq!(smv.clone().get(), 5);
858
        assert_eq!(smv, SendMeVersion::try_from(5).unwrap());
859
    }
860
}