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 std::time::Duration;
49
use thiserror::Error;
50

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

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

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

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

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

            
112
935440
        BoundedInt32 { value }
113
935440
    }
114

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
402
    /// Map the inner value (useful for conversion)
403
    ///
404
    /// ```
405
    /// use tor_units::{BoundedInt32, IntegerSeconds};
406
    ///
407
    /// let value: IntegerSeconds<i32> = 42.into();
408
    /// let value: IntegerSeconds<BoundedInt32<0,1000>>
409
    ///     = value.try_map(TryInto::try_into).unwrap();
410
    /// ```
411
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerSeconds<U>, E>
412
    where
413
        F: FnOnce(T) -> Result<U, E>,
414
    {
415
        Ok(IntegerSeconds::new(f(self.value)?))
416
    }
417
}
418

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

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

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

            
442
impl<T> IntegerMinutes<T> {
443
    /// Public Constructor
444
29832
    pub fn new(value: T) -> Self {
445
29832
        IntegerMinutes { value }
446
29832
    }
447

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

            
456
    /// Map the inner value (useful for conversion)
457
    ///
458
    /// ```
459
    /// use tor_units::{BoundedInt32, IntegerMinutes};
460
    ///
461
    /// let value: IntegerMinutes<i32> = 42.into();
462
    /// let value: IntegerMinutes<BoundedInt32<0,1000>>
463
    ///     = value.try_map(TryInto::try_into).unwrap();
464
    /// ```
465
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMinutes<U>, E>
466
    where
467
        F: FnOnce(T) -> Result<U, E>,
468
    {
469
        Ok(IntegerMinutes::new(f(self.value)?))
470
    }
471
}
472

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

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

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

            
502
impl<T> IntegerDays<T> {
503
    /// Public Constructor
504
30622
    pub fn new(value: T) -> Self {
505
30622
        IntegerDays { value }
506
30622
    }
507

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

            
516
    /// Map the inner value (useful for conversion)
517
    ///
518
    /// ```
519
    /// use tor_units::{BoundedInt32, IntegerDays};
520
    ///
521
    /// let value: IntegerDays<i32> = 42.into();
522
    /// let value: IntegerDays<BoundedInt32<0,1000>>
523
    ///     = value.try_map(TryInto::try_into).unwrap();
524
    /// ```
525
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerDays<U>, E>
526
    where
527
        F: FnOnce(T) -> Result<U, E>,
528
    {
529
        Ok(IntegerDays::new(f(self.value)?))
530
    }
531
}
532

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

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

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

            
557
impl SendMeVersion {
558
    /// Public Constructor
559
23484
    pub fn new(value: u8) -> Self {
560
23484
        SendMeVersion(value)
561
23484
    }
562

            
563
    /// Helper
564
204
    pub fn get(&self) -> u8 {
565
204
        self.0
566
204
    }
567
}
568

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

            
577
#[cfg(test)]
578
mod tests {
579
    #![allow(clippy::unwrap_used)]
580
    use float_cmp::assert_approx_eq;
581

            
582
    use super::*;
583

            
584
    type TestFoo = BoundedInt32<1, 5>;
585
    type TestBar = BoundedInt32<-45, 17>;
586

            
587
    //make_parameter_type! {TestFoo(3,)}
588
    #[test]
589
    fn entire_range_parsed() {
590
        let x: TestFoo = "1".parse().unwrap();
591
        assert!(x.get() == 1);
592
        let x: TestFoo = "2".parse().unwrap();
593
        assert!(x.get() == 2);
594
        let x: TestFoo = "3".parse().unwrap();
595
        assert!(x.get() == 3);
596
        let x: TestFoo = "4".parse().unwrap();
597
        assert!(x.get() == 4);
598
        let x: TestFoo = "5".parse().unwrap();
599
        assert!(x.get() == 5);
600
    }
601

            
602
    #[test]
603
    fn saturating() {
604
        let x: TestFoo = TestFoo::saturating_new(1000);
605
        let x_val: i32 = x.into();
606
        assert!(x_val == TestFoo::UPPER);
607
        let x: TestFoo = TestFoo::saturating_new(0);
608
        let x_val: i32 = x.into();
609
        assert!(x_val == TestFoo::LOWER);
610
    }
611
    #[test]
612
    fn saturating_string() {
613
        let x: TestFoo = TestFoo::saturating_from_str("1000").unwrap();
614
        let x_val: i32 = x.into();
615
        assert!(x_val == TestFoo::UPPER);
616
        let x: TestFoo = TestFoo::saturating_from_str("0").unwrap();
617
        let x_val: i32 = x.into();
618
        assert!(x_val == TestFoo::LOWER);
619
    }
620

            
621
    #[test]
622
    #[should_panic]
623
    fn uninhabited_saturating_new() {
624
        // This value should be uncreatable.
625
        let _: BoundedInt32<10, 5> = BoundedInt32::saturating_new(7);
626
    }
627

            
628
    #[test]
629
    fn uninhabited_from_string() {
630
        let v: Result<BoundedInt32<10, 5>, Error> = BoundedInt32::saturating_from_str("7");
631
        assert!(matches!(v, Err(Error::Uninhabited)));
632
    }
633

            
634
    #[test]
635
    fn errors_correct() {
636
        let x: Result<TestBar, Error> = "1000".parse();
637
        assert!(x.unwrap_err() == Error::AboveUpperBound(1000, TestBar::UPPER));
638
        let x: Result<TestBar, Error> = "-1000".parse();
639
        assert!(x.unwrap_err() == Error::BelowLowerBound(-1000, TestBar::LOWER));
640
        let x: Result<TestBar, Error> = "xyz".parse();
641
        assert!(x.unwrap_err() == Error::Unrepresentable);
642
    }
643

            
644
    #[test]
645
    fn display() {
646
        let v = BoundedInt32::<99, 1000>::checked_new(345).unwrap();
647
        assert_eq!(v.to_string(), "345".to_string());
648
    }
649

            
650
    #[test]
651
    #[should_panic]
652
    fn checked_too_high() {
653
        let _: TestBar = "1000".parse().unwrap();
654
    }
655

            
656
    #[test]
657
    #[should_panic]
658
    fn checked_too_low() {
659
        let _: TestBar = "-46".parse().unwrap();
660
    }
661

            
662
    #[test]
663
    fn bounded_to_u64() {
664
        let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
665
        let u: u64 = b.try_into().unwrap();
666
        assert_eq!(u, 77);
667

            
668
        let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(-77).unwrap();
669
        let u: Result<u64, Error> = b.try_into();
670
        assert!(u.is_err());
671
    }
672

            
673
    #[test]
674
    fn bounded_to_f64() {
675
        let x: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
676
        let f: f64 = x.into();
677
        assert_approx_eq!(f64, f, 77.0);
678
    }
679

            
680
    #[test]
681
    fn bounded_from_i32() {
682
        let x: Result<BoundedInt32<-100, 100>, _> = 50.try_into();
683
        let y: i32 = x.unwrap().into();
684
        assert_eq!(y, 50);
685

            
686
        let x: Result<BoundedInt32<-100, 100>, _> = 1000.try_into();
687
        assert!(x.is_err());
688
    }
689

            
690
    #[test]
691
    fn into_bool() {
692
        let zero: BoundedInt32<0, 1> = BoundedInt32::saturating_from(0);
693
        let one: BoundedInt32<0, 1> = BoundedInt32::saturating_from(1);
694

            
695
        let f: bool = zero.into();
696
        let t: bool = one.into();
697
        assert!(!f);
698
        assert!(t);
699
    }
700

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

            
708
        let a: u8 = zero.into();
709
        let b: u8 = one.into();
710
        let c: u8 = ninety.into();
711
        let d: u8 = max.into();
712

            
713
        assert_eq!(a, 0);
714
        assert_eq!(b, 1);
715
        assert_eq!(c, 90);
716
        assert_eq!(d, 255);
717
    }
718

            
719
    #[test]
720
    fn into_u32() {
721
        let zero: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(0);
722
        let one: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1);
723
        let ninety: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(90);
724
        let max: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1000);
725

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

            
731
        let zero: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(0);
732
        let one: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1);
733
        let ninety: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(90);
734
        let max: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1000);
735

            
736
        assert_eq!(u32::from(zero), 1);
737
        assert_eq!(u32::from(one), 1);
738
        assert_eq!(u32::from(ninety), 90);
739
        assert_eq!(u32::from(max), 1000);
740
    }
741

            
742
    #[test]
743
    fn try_into_usize() {
744
        let b0: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(0);
745
        let b100: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(100);
746
        let bn5: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(-5);
747
        assert_eq!(usize::try_from(b0), Ok(0_usize));
748
        assert_eq!(usize::try_from(b100), Ok(100_usize));
749
        assert_eq!(usize::try_from(bn5), Err(Error::Negative));
750
    }
751

            
752
    #[test]
753
    fn percents() {
754
        type Pct = Percentage<u8>;
755
        let p = Pct::new(100);
756
        assert_eq!(p.as_percent(), 100);
757
        assert_approx_eq!(f64, p.as_fraction(), 1.0);
758

            
759
        let p = Pct::new(0);
760
        assert_eq!(p.as_percent(), 0);
761
        assert_approx_eq!(f64, p.as_fraction(), 0.0);
762

            
763
        let p = Pct::new(25);
764
        assert_eq!(p.as_percent(), 25);
765
        assert_eq!(p.clone(), p);
766
        assert_approx_eq!(f64, p.as_fraction(), 0.25);
767

            
768
        type BPct = Percentage<BoundedInt32<0, 100>>;
769
        assert_eq!(BPct::try_from(99).unwrap().as_percent().get(), 99);
770
    }
771

            
772
    #[test]
773
    fn milliseconds() {
774
        type Msec = IntegerMilliseconds<i32>;
775

            
776
        let ms = Msec::new(500);
777
        let d: Result<Duration, _> = ms.try_into();
778
        assert_eq!(d.unwrap(), Duration::from_millis(500));
779
        assert_eq!(Duration::try_from(ms * 2).unwrap(), Duration::from_secs(1));
780

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

            
785
        type BMSec = IntegerMilliseconds<BoundedInt32<0, 1000>>;
786
        let half_sec = BMSec::try_from(500).unwrap();
787
        assert_eq!(
788
            Duration::try_from(half_sec).unwrap(),
789
            Duration::from_millis(500)
790
        );
791
        assert!(BMSec::try_from(1001).is_err());
792
    }
793

            
794
    #[test]
795
    fn seconds() {
796
        type Sec = IntegerSeconds<i32>;
797

            
798
        let ms = Sec::new(500);
799
        let d: Result<Duration, _> = ms.try_into();
800
        assert_eq!(d.unwrap(), Duration::from_secs(500));
801

            
802
        let ms = Sec::new(-100);
803
        let d: Result<Duration, _> = ms.try_into();
804
        assert!(d.is_err());
805

            
806
        type BSec = IntegerSeconds<BoundedInt32<0, 3600>>;
807
        let half_hour = BSec::try_from(1800).unwrap();
808
        assert_eq!(
809
            Duration::try_from(half_hour).unwrap(),
810
            Duration::from_secs(1800)
811
        );
812
        assert!(BSec::try_from(9999).is_err());
813
        assert_eq!(half_hour.clone(), half_hour);
814
    }
815

            
816
    #[test]
817
    fn minutes() {
818
        type Min = IntegerMinutes<i32>;
819

            
820
        let t = Min::new(500);
821
        let d: Duration = t.try_into().unwrap();
822
        assert_eq!(d, Duration::from_secs(500 * 60));
823

            
824
        let t = Min::new(-100);
825
        let d: Result<Duration, _> = t.try_into();
826
        assert_eq!(d, Err(Error::Overflow));
827

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

            
832
        type BMin = IntegerMinutes<BoundedInt32<10, 30>>;
833
        assert_eq!(
834
            BMin::new(17_i32.try_into().unwrap()),
835
            BMin::try_from(17).unwrap()
836
        );
837
    }
838

            
839
    #[test]
840
    fn days() {
841
        type Days = IntegerDays<i32>;
842

            
843
        let t = Days::new(500);
844
        let d: Duration = t.try_into().unwrap();
845
        assert_eq!(d, Duration::from_secs(500 * 86400));
846

            
847
        let t = Days::new(-100);
848
        let d: Result<Duration, _> = t.try_into();
849
        assert_eq!(d, Err(Error::Overflow));
850

            
851
        let t = IntegerDays::<u64>::new(u64::MAX);
852
        let d: Result<Duration, _> = t.try_into();
853
        assert_eq!(d, Err(Error::Overflow));
854

            
855
        type BDays = IntegerDays<BoundedInt32<10, 30>>;
856
        assert_eq!(
857
            BDays::new(17_i32.try_into().unwrap()),
858
            BDays::try_from(17).unwrap()
859
        );
860
    }
861

            
862
    #[test]
863
    fn sendme() {
864
        let smv = SendMeVersion::new(5);
865
        assert_eq!(smv.get(), 5);
866
        assert_eq!(smv.clone().get(), 5);
867
        assert_eq!(smv, SendMeVersion::try_from(5).unwrap());
868
    }
869
}