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
#![deny(clippy::mod_module_files)]
38
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
39
#![allow(clippy::uninlined_format_args)]
40
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
41
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
42
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
43
#![allow(clippy::needless_lifetimes)] // See arti#1765
44
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
45

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

            
48
use serde::{Deserialize, Serialize};
49
use std::time::Duration;
50
use thiserror::Error;
51

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

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

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

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

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

            
113
1099157
        BoundedInt32 { value }
114
1099157
    }
115

            
116
    /// Return the lower bound value of this bounded i32.
117
    ///
118
    /// This always return [`Self::LOWER`].
119
44
    pub const fn lower(&self) -> i32 {
120
44
        LOWER
121
44
    }
122

            
123
    /// Return the lower bound value of this bounded i32.
124
    ///
125
    /// This always return [`Self::LOWER`].
126
44
    pub const fn upper(&self) -> i32 {
127
44
        UPPER
128
44
    }
129

            
130
    /// Return the underlying i32 value.
131
    ///
132
    /// This value will always be between [`Self::LOWER`] and [`Self::UPPER`],
133
    /// inclusive.
134
33689
    pub fn get(&self) -> i32 {
135
33689
        self.value
136
33689
    }
137

            
138
    /// If `val` is within range, return a new `BoundedInt32` wrapping
139
    /// it; otherwise, clamp it to the upper or lower bound as
140
    /// appropriate.
141
11359
    pub fn saturating_new(val: i32) -> Self {
142
11359
        Self::unchecked_new(Self::clamp(val))
143
11359
    }
144

            
145
    /// If `val` is an acceptable value inside the range for this type,
146
    /// return a new [`BoundedInt32`].  Otherwise return an error.
147
1087776
    pub fn checked_new(val: i32) -> Result<Self, Error> {
148
1087776
        if val > UPPER {
149
10
            Err(Error::AboveUpperBound(val, UPPER))
150
1087766
        } else if val < LOWER {
151
4
            Err(Error::BelowLowerBound(val, LOWER))
152
        } else {
153
1087762
            Ok(BoundedInt32::unchecked_new(val))
154
        }
155
1087776
    }
156

            
157
    /// This private function clamps an input to the acceptable range.
158
11397
    fn clamp(val: i32) -> i32 {
159
11397
        Ord::clamp(val, LOWER, UPPER)
160
11397
    }
161

            
162
    /// Convert from the underlying type, clamping to the upper or
163
    /// lower bound if needed.
164
    ///
165
    /// # Panics
166
    ///
167
    /// This function will panic if UPPER < LOWER.
168
38
    pub fn saturating_from(val: i32) -> Self {
169
38
        Self::unchecked_new(Self::clamp(val))
170
38
    }
171

            
172
    /// Convert from a string, clamping to the upper or lower bound if needed.
173
    ///
174
    /// # Limitations
175
    ///
176
    /// If the input is a number that cannot be represented as an i32,
177
    /// then we return an error instead of clamping it.
178
6
    pub fn saturating_from_str(s: &str) -> Result<Self, Error> {
179
6
        if UPPER < LOWER {
180
            // The compiler should optimize this block out at compile time.
181
2
            return Err(Error::Uninhabited);
182
4
        }
183
4
        let val: i32 = s.parse().map_err(|_| Error::Unrepresentable)?;
184
4
        Ok(Self::saturating_from(val))
185
6
    }
186
}
187

            
188
impl<const L: i32, const U: i32> std::fmt::Display for BoundedInt32<L, U> {
189
2
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190
2
        write!(f, "{}", self.value)
191
2
    }
192
}
193

            
194
impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for i32 {
195
10
    fn from(val: BoundedInt32<L, U>) -> i32 {
196
10
        val.value
197
10
    }
198
}
199

            
200
impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for f64 {
201
11000
    fn from(val: BoundedInt32<L, U>) -> f64 {
202
11000
        val.value.into()
203
11000
    }
204
}
205

            
206
impl<const L: i32, const H: i32> TryFrom<i32> for BoundedInt32<L, H> {
207
    type Error = Error;
208
1035078
    fn try_from(val: i32) -> Result<Self, Self::Error> {
209
1035078
        Self::checked_new(val)
210
1035078
    }
211
}
212

            
213
impl<const L: i32, const H: i32> std::str::FromStr for BoundedInt32<L, H> {
214
    type Err = Error;
215
20
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
216
20
        Self::checked_new(s.parse().map_err(|_| Error::Unrepresentable)?)
217
20
    }
218
}
219

            
220
impl From<BoundedInt32<0, 1>> for bool {
221
764984
    fn from(val: BoundedInt32<0, 1>) -> bool {
222
764984
        val.value == 1
223
764984
    }
224
}
225

            
226
impl From<BoundedInt32<0, 255>> for u8 {
227
8
    fn from(val: BoundedInt32<0, 255>) -> u8 {
228
8
        val.value as u8
229
8
    }
230
}
231

            
232
impl<const L: i32, const H: i32> From<BoundedInt32<L, H>> for u32 {
233
14480
    fn from(val: BoundedInt32<L, H>) -> u32 {
234
14480
        val.value as u32
235
14480
    }
236
}
237

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

            
249
impl<const L: i32, const H: i32> TryFrom<BoundedInt32<L, H>> for usize {
250
    type Error = Error;
251
10852
    fn try_from(val: BoundedInt32<L, H>) -> Result<Self, Self::Error> {
252
10852
        if val.value < 0 {
253
2
            Err(Error::Negative)
254
        } else {
255
10850
            Ok(val.value as usize)
256
        }
257
10852
    }
258
}
259

            
260
/// A percentage value represented as a number.
261
///
262
/// This type wraps an underlying numeric type, and ensures that callers
263
/// are clear whether they want a _fraction_, or a _percentage_.
264
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
265
pub struct Percentage<T: Copy + Into<f64>> {
266
    /// The underlying percentage value.
267
    value: T,
268
}
269

            
270
impl<T: Copy + Into<f64>> Percentage<T> {
271
    /// Create a new `IntPercentage` from the underlying percentage.
272
119346
    pub fn new(value: T) -> Self {
273
119346
        Self { value }
274
119346
    }
275

            
276
    /// Return this value as a (possibly improper) fraction.
277
    ///
278
    /// ```
279
    /// use tor_units::Percentage;
280
    /// let pct_200 = Percentage::<u8>::new(200);
281
    /// let pct_100 = Percentage::<u8>::new(100);
282
    /// let pct_50 = Percentage::<u8>::new(50);
283
    ///
284
    /// assert_eq!(pct_200.as_fraction(), 2.0);
285
    /// assert_eq!(pct_100.as_fraction(), 1.0);
286
    /// assert_eq!(pct_50.as_fraction(), 0.5);
287
    /// // Note: don't actually compare f64 with ==.
288
    /// ```
289
11004
    pub fn as_fraction(self) -> f64 {
290
11004
        self.value.into() / 100.0
291
11004
    }
292

            
293
    /// Return this value as a percentage.
294
    ///
295
    /// ```
296
    /// use tor_units::Percentage;
297
    /// let pct_200 = Percentage::<u8>::new(200);
298
    /// let pct_100 = Percentage::<u8>::new(100);
299
    /// let pct_50 = Percentage::<u8>::new(50);
300
    ///
301
    /// assert_eq!(pct_200.as_percent(), 200);
302
    /// assert_eq!(pct_100.as_percent(), 100);
303
    /// assert_eq!(pct_50.as_percent(), 50);
304
    /// ```
305
332
    pub fn as_percent(self) -> T {
306
332
        self.value
307
332
    }
308
}
309

            
310
impl<const H: i32, const L: i32> TryFrom<i32> for Percentage<BoundedInt32<H, L>> {
311
    type Error = Error;
312
117622
    fn try_from(v: i32) -> Result<Self, Error> {
313
117622
        Ok(Percentage::new(v.try_into()?))
314
117622
    }
315
}
316

            
317
// TODO: There is a bunch of code duplication among these "IntegerTimeUnits"
318
// section.
319

            
320
#[derive(
321
    Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
322
)]
323
/// This type represents an integer number of milliseconds.
324
///
325
/// The underlying type should usually implement `TryInto<u64>`.
326
pub struct IntegerMilliseconds<T> {
327
    /// Interior Value. Should implement `TryInto<u64>` to be useful.
328
    value: T,
329
}
330

            
331
impl<T> IntegerMilliseconds<T> {
332
    /// Public Constructor
333
87884
    pub fn new(value: T) -> Self {
334
87884
        IntegerMilliseconds { value }
335
87884
    }
336

            
337
    /// Deconstructor
338
    ///
339
    /// Use only in contexts where it's no longer possible to
340
    /// use the Rust type system to ensure secs vs ms vs us correctness.
341
4776
    pub fn as_millis(self) -> T {
342
4776
        self.value
343
4776
    }
344

            
345
    /// Map the inner value (useful for conversion)
346
    ///
347
    /// # Example
348
    ///
349
    /// ```
350
    /// use tor_units::{BoundedInt32, IntegerMilliseconds};
351
    ///
352
    /// let value: IntegerMilliseconds<i32> = 42.into();
353
    /// let value: IntegerMilliseconds<BoundedInt32<0,1000>>
354
    ///     = value.try_map(TryInto::try_into).unwrap();
355
    /// ```
356
4708
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMilliseconds<U>, E>
357
4708
    where
358
4708
        F: FnOnce(T) -> Result<U, E>,
359
4708
    {
360
4708
        Ok(IntegerMilliseconds::new(f(self.value)?))
361
4708
    }
362
}
363

            
364
impl<T: TryInto<u64>> TryFrom<IntegerMilliseconds<T>> for Duration {
365
    type Error = <T as TryInto<u64>>::Error;
366
32
    fn try_from(val: IntegerMilliseconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
367
32
        Ok(Self::from_millis(val.value.try_into()?))
368
32
    }
369
}
370

            
371
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMilliseconds<BoundedInt32<H, L>> {
372
    type Error = Error;
373
82338
    fn try_from(v: i32) -> Result<Self, Error> {
374
82338
        Ok(IntegerMilliseconds::new(v.try_into()?))
375
82338
    }
376
}
377

            
378
#[derive(
379
    Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
380
)]
381
/// This type represents an integer number of seconds.
382
///
383
/// The underlying type should usually implement `TryInto<u64>`.
384
pub struct IntegerSeconds<T> {
385
    /// Interior Value. Should implement `TryInto<u64>` to be useful.
386
    value: T,
387
}
388

            
389
impl<T> IntegerSeconds<T> {
390
    /// Public Constructor
391
141162
    pub fn new(value: T) -> Self {
392
141162
        IntegerSeconds { value }
393
141162
    }
394

            
395
    /// Deconstructor
396
    ///
397
    /// Use only in contexts where it's no longer possible to
398
    /// use the Rust type system to ensure secs vs ms vs us correctness.
399
    pub fn as_secs(self) -> T {
400
        self.value
401
    }
402

            
403
    /// Map the inner value (useful for conversion)
404
    ///
405
    /// ```
406
    /// use tor_units::{BoundedInt32, IntegerSeconds};
407
    ///
408
    /// let value: IntegerSeconds<i32> = 42.into();
409
    /// let value: IntegerSeconds<BoundedInt32<0,1000>>
410
    ///     = value.try_map(TryInto::try_into).unwrap();
411
    /// ```
412
    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

            
420
impl<T: TryInto<u64>> TryFrom<IntegerSeconds<T>> for Duration {
421
    type Error = <T as TryInto<u64>>::Error;
422
14196
    fn try_from(val: IntegerSeconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
423
14196
        Ok(Self::from_secs(val.value.try_into()?))
424
14196
    }
425
}
426

            
427
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerSeconds<BoundedInt32<H, L>> {
428
    type Error = Error;
429
141148
    fn try_from(v: i32) -> Result<Self, Error> {
430
141148
        Ok(IntegerSeconds::new(v.try_into()?))
431
141148
    }
432
}
433

            
434
#[derive(Deserialize, Serialize)] //
435
#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
436
/// This type represents an integer number of minutes.
437
///
438
/// The underlying type should usually implement `TryInto<u64>`.
439
pub struct IntegerMinutes<T> {
440
    /// Interior Value. Should Implement `TryInto<u64>` to be useful.
441
    value: T,
442
}
443

            
444
impl<T> IntegerMinutes<T> {
445
    /// Public Constructor
446
33677
    pub fn new(value: T) -> Self {
447
33677
        IntegerMinutes { value }
448
33677
    }
449

            
450
    /// Deconstructor
451
    ///
452
    /// Use only in contexts where it's no longer possible to
453
    /// use the Rust type system to ensure secs vs ms vs us correctness.
454
204530
    pub fn as_minutes(self) -> T {
455
204530
        self.value
456
204530
    }
457

            
458
    /// Map the inner value (useful for conversion)
459
    ///
460
    /// ```
461
    /// use tor_units::{BoundedInt32, IntegerMinutes};
462
    ///
463
    /// let value: IntegerMinutes<i32> = 42.into();
464
    /// let value: IntegerMinutes<BoundedInt32<0,1000>>
465
    ///     = value.try_map(TryInto::try_into).unwrap();
466
    /// ```
467
    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

            
475
impl<T: TryInto<u64>> TryFrom<IntegerMinutes<T>> for Duration {
476
    type Error = Error;
477
14570
    fn try_from(val: IntegerMinutes<T>) -> Result<Self, Error> {
478
        /// Number of seconds in a single minute.
479
        const SECONDS_PER_MINUTE: u64 = 60;
480
14571
        let minutes: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
481
14568
        let seconds = minutes
482
14568
            .checked_mul(SECONDS_PER_MINUTE)
483
14568
            .ok_or(Error::Overflow)?;
484
14566
        Ok(Self::from_secs(seconds))
485
14570
    }
486
}
487

            
488
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMinutes<BoundedInt32<H, L>> {
489
    type Error = Error;
490
11764
    fn try_from(v: i32) -> Result<Self, Error> {
491
11764
        Ok(IntegerMinutes::new(v.try_into()?))
492
11764
    }
493
}
494

            
495
#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
496
/// This type represents an integer number of days.
497
///
498
/// The underlying type should usually implement `TryInto<u64>`.
499
pub struct IntegerDays<T> {
500
    /// Interior Value. Should Implement `TryInto<u64>` to be useful.
501
    value: T,
502
}
503

            
504
impl<T> IntegerDays<T> {
505
    /// Public Constructor
506
35302
    pub fn new(value: T) -> Self {
507
35302
        IntegerDays { value }
508
35302
    }
509

            
510
    /// Deconstructor
511
    ///
512
    /// Use only in contexts where it's no longer possible to
513
    /// use the Rust type system to ensure secs vs ms vs us correctness.
514
    pub fn as_days(self) -> T {
515
        self.value
516
    }
517

            
518
    /// Map the inner value (useful for conversion)
519
    ///
520
    /// ```
521
    /// use tor_units::{BoundedInt32, IntegerDays};
522
    ///
523
    /// let value: IntegerDays<i32> = 42.into();
524
    /// let value: IntegerDays<BoundedInt32<0,1000>>
525
    ///     = value.try_map(TryInto::try_into).unwrap();
526
    /// ```
527
    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

            
535
impl<T: TryInto<u64>> TryFrom<IntegerDays<T>> for Duration {
536
    type Error = Error;
537
3582
    fn try_from(val: IntegerDays<T>) -> Result<Self, Error> {
538
        /// Number of seconds in a single day.
539
        const SECONDS_PER_DAY: u64 = 86400;
540
3583
        let days: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
541
3580
        let seconds = days.checked_mul(SECONDS_PER_DAY).ok_or(Error::Overflow)?;
542
3578
        Ok(Self::from_secs(seconds))
543
3582
    }
544
}
545

            
546
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerDays<BoundedInt32<H, L>> {
547
    type Error = Error;
548
35288
    fn try_from(v: i32) -> Result<Self, Error> {
549
35288
        Ok(IntegerDays::new(v.try_into()?))
550
35288
    }
551
}
552

            
553
/// A SendMe Version
554
///
555
/// DOCDOC: Explain why this needs to have its own type, or remove it.
556
#[derive(Clone, Copy, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
557
pub struct SendMeVersion(u8);
558

            
559
impl SendMeVersion {
560
    /// Public Constructor
561
27006
    pub fn new(value: u8) -> Self {
562
27006
        SendMeVersion(value)
563
27006
    }
564

            
565
    /// Helper
566
234
    pub fn get(&self) -> u8 {
567
234
        self.0
568
234
    }
569
}
570

            
571
impl TryFrom<i32> for SendMeVersion {
572
    type Error = Error;
573
26774
    fn try_from(v: i32) -> Result<Self, Error> {
574
26774
        let val_u8 = BoundedInt32::<0, 255>::checked_new(v)?;
575
26774
        Ok(SendMeVersion::new(val_u8.get() as u8))
576
26774
    }
577
}
578

            
579
#[cfg(test)]
580
mod 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
    //make_parameter_type! {TestFoo(3,)}
590
    #[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
        // This value should be uncreatable.
627
        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
}