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
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
45
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
46

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

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

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

            
56
/// Conversion errors from converting a value into a [`BoundedInt32`].
57
#[derive(Debug, Clone, PartialEq, Eq, Error)]
58
#[non_exhaustive]
59
pub enum Error {
60
    /// A passed value was below the lower bound for the type.
61
    #[error("Value {0} was below the lower bound {1} for this type")]
62
    BelowLowerBound(i32, i32),
63
    /// A passed value was above the upper bound for the type.
64
    #[error("Value {0} was above the lower bound {1} for this type")]
65
    AboveUpperBound(i32, i32),
66
    /// Tried to convert a negative value to an unsigned type.
67
    #[error("Tried to convert a negative value to an unsigned type")]
68
    Negative,
69
    /// Tried to parse a value that was not representable as the
70
    /// underlying type.
71
    #[error("Value could not be represented as an i32")]
72
    Unrepresentable,
73
    /// We encountered some kind of integer overflow when converting a number.
74
    #[error("Integer overflow")]
75
    Overflow,
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 were to try to instantiate this type with LOWER > UPPER,
85
/// you would get an uninhabitable type.
86
/// Attempting to construct a value with a type with LOWER > UPPER
87
/// will result in a compile-time error;
88
/// though there may not be a compiler error if the code that constructs the value is
89
/// dead code and is optimized away.
90
/// It would be better if we could prevent such types from being named.
91
//
92
// [TODO: If you need a Bounded* for some type other than i32, ask nickm:
93
// he has an implementation kicking around.]
94
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
95
#[cfg_attr(
96
    feature = "memquota-memcost",
97
    derive(Deftly),
98
    derive_deftly(HasMemoryCost)
99
)]
100
pub struct BoundedInt32<const LOWER: i32, const UPPER: i32> {
101
    /// Interior Value
102
    value: i32,
103
}
104

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

            
111
    /// Private constructor function for this type.
112
1432140
    fn unchecked_new(value: i32) -> Self {
113
        // If there is a code path leading to this function that remains after dead code elimination,
114
        // this will ensures LOWER <= UPPER at build time.
115
        const { assert!(LOWER <= UPPER) };
116

            
117
1432140
        BoundedInt32 { value }
118
1432140
    }
119

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

            
127
    /// Return the lower bound value of this bounded i32.
128
    ///
129
    /// This always return [`Self::LOWER`].
130
22
    pub const fn upper(&self) -> i32 {
131
22
        UPPER
132
22
    }
133

            
134
    /// Return the underlying i32 value.
135
    ///
136
    /// This value will always be between [`Self::LOWER`] and [`Self::UPPER`],
137
    /// inclusive.
138
39721
    pub fn get(&self) -> i32 {
139
39721
        self.value
140
39721
    }
141

            
142
    /// Return the underlying u32 value, if [`Self::LOWER`] is non-negative.
143
    ///
144
    /// If [`Self::LOWER`] is negative, this will panic at build-time.
145
    ///
146
    /// This value will always be between [`Self::LOWER`] and [`Self::UPPER`],
147
    /// inclusive.
148
110
    pub fn get_u32(&self) -> u32 {
149
        const { assert!(LOWER >= 0) };
150
110
        self.value as u32
151
110
    }
152

            
153
    /// If `val` is within range, return a new `BoundedInt32` wrapping
154
    /// it; otherwise, clamp it to the upper or lower bound as
155
    /// appropriate.
156
55
    pub fn saturating_new(val: i32) -> Self {
157
55
        Self::unchecked_new(Self::clamp(val))
158
55
    }
159

            
160
    /// If `val` is an acceptable value inside the range for this type,
161
    /// return a new [`BoundedInt32`].  Otherwise return an error.
162
1432112
    pub fn checked_new(val: i32) -> Result<Self, Error> {
163
1432112
        if val > UPPER {
164
61
            Err(Error::AboveUpperBound(val, UPPER))
165
1432051
        } else if val < LOWER {
166
4
            Err(Error::BelowLowerBound(val, LOWER))
167
        } else {
168
1432047
            Ok(BoundedInt32::unchecked_new(val))
169
        }
170
1432112
    }
171

            
172
    /// This private function clamps an input to the acceptable range.
173
93
    fn clamp(val: i32) -> i32 {
174
93
        Ord::clamp(val, LOWER, UPPER)
175
93
    }
176

            
177
    /// Convert from the underlying type, clamping to the upper or
178
    /// lower bound if needed.
179
    ///
180
    /// # Panics
181
    ///
182
    /// This function will panic if UPPER < LOWER.
183
38
    pub fn saturating_from(val: i32) -> Self {
184
38
        Self::unchecked_new(Self::clamp(val))
185
38
    }
186

            
187
    /// Convert from a string, clamping to the upper or lower bound if needed.
188
    ///
189
    /// # Limitations
190
    ///
191
    /// If the input is a number that cannot be represented as an i32,
192
    /// then we return an error instead of clamping it.
193
4
    pub fn saturating_from_str(s: &str) -> Result<Self, Error> {
194
4
        let val: i32 = s.parse().map_err(|_| Error::Unrepresentable)?;
195
4
        Ok(Self::saturating_from(val))
196
4
    }
197
}
198

            
199
impl<const L: i32, const U: i32> std::fmt::Display for BoundedInt32<L, U> {
200
2
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201
2
        write!(f, "{}", self.value)
202
2
    }
203
}
204

            
205
impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for i32 {
206
10
    fn from(val: BoundedInt32<L, U>) -> i32 {
207
10
        val.value
208
10
    }
209
}
210

            
211
impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for f64 {
212
14563
    fn from(val: BoundedInt32<L, U>) -> f64 {
213
14563
        val.value.into()
214
14563
    }
215
}
216

            
217
impl<const L: i32, const H: i32> TryFrom<i32> for BoundedInt32<L, H> {
218
    type Error = Error;
219
1351148
    fn try_from(val: i32) -> Result<Self, Self::Error> {
220
1351148
        Self::checked_new(val)
221
1351148
    }
222
}
223

            
224
impl<const L: i32, const H: i32> std::str::FromStr for BoundedInt32<L, H> {
225
    type Err = Error;
226
20
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
227
20
        Self::checked_new(s.parse().map_err(|_| Error::Unrepresentable)?)
228
20
    }
229
}
230

            
231
impl From<BoundedInt32<0, 1>> for bool {
232
1137768
    fn from(val: BoundedInt32<0, 1>) -> bool {
233
1137768
        val.value == 1
234
1137768
    }
235
}
236

            
237
impl From<BoundedInt32<0, 255>> for u8 {
238
8
    fn from(val: BoundedInt32<0, 255>) -> u8 {
239
8
        val.value as u8
240
8
    }
241
}
242

            
243
impl<const L: i32, const H: i32> From<BoundedInt32<L, H>> for u32 {
244
17767
    fn from(val: BoundedInt32<L, H>) -> u32 {
245
17767
        val.value as u32
246
17767
    }
247
}
248

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

            
260
impl<const L: i32, const H: i32> TryFrom<BoundedInt32<L, H>> for usize {
261
    type Error = Error;
262
14447
    fn try_from(val: BoundedInt32<L, H>) -> Result<Self, Self::Error> {
263
14447
        if val.value < 0 {
264
2
            Err(Error::Negative)
265
        } else {
266
14445
            Ok(val.value as usize)
267
        }
268
14447
    }
269
}
270

            
271
/// A percentage value represented as a number.
272
///
273
/// This type wraps an underlying numeric type, and ensures that callers
274
/// are clear whether they want a _fraction_, or a _percentage_.
275
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
276
pub struct Percentage<T: Copy + Into<f64>> {
277
    /// The underlying percentage value.
278
    value: T,
279
}
280

            
281
impl<T: Copy + Into<f64>> Percentage<T> {
282
    /// Create a new `IntPercentage` from the underlying percentage.
283
154133
    pub fn new(value: T) -> Self {
284
154133
        Self { value }
285
154133
    }
286

            
287
    /// Return this value as a (possibly improper) fraction.
288
    ///
289
    /// ```
290
    /// use tor_units::Percentage;
291
    /// let pct_200 = Percentage::<u8>::new(200);
292
    /// let pct_100 = Percentage::<u8>::new(100);
293
    /// let pct_50 = Percentage::<u8>::new(50);
294
    ///
295
    /// assert_eq!(pct_200.as_fraction(), 2.0);
296
    /// assert_eq!(pct_100.as_fraction(), 1.0);
297
    /// assert_eq!(pct_50.as_fraction(), 0.5);
298
    /// // Note: don't actually compare f64 with ==.
299
    /// ```
300
14567
    pub fn as_fraction(self) -> f64 {
301
14567
        self.value.into() / 100.0
302
14567
    }
303

            
304
    /// Return this value as a percentage.
305
    ///
306
    /// ```
307
    /// use tor_units::Percentage;
308
    /// let pct_200 = Percentage::<u8>::new(200);
309
    /// let pct_100 = Percentage::<u8>::new(100);
310
    /// let pct_50 = Percentage::<u8>::new(50);
311
    ///
312
    /// assert_eq!(pct_200.as_percent(), 200);
313
    /// assert_eq!(pct_100.as_percent(), 100);
314
    /// assert_eq!(pct_50.as_percent(), 50);
315
    /// ```
316
410
    pub fn as_percent(self) -> T {
317
410
        self.value
318
410
    }
319
}
320

            
321
impl<const H: i32, const L: i32> TryFrom<i32> for Percentage<BoundedInt32<H, L>> {
322
    type Error = Error;
323
151659
    fn try_from(v: i32) -> Result<Self, Error> {
324
151659
        Ok(Percentage::new(v.try_into()?))
325
151659
    }
326
}
327

            
328
// TODO: There is a bunch of code duplication among these "IntegerTimeUnits"
329
// section.
330

            
331
#[derive(
332
    Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
333
)]
334
/// This type represents an integer number of milliseconds.
335
///
336
/// The underlying type should usually implement `TryInto<u64>`.
337
pub struct IntegerMilliseconds<T> {
338
    /// Interior Value. Should implement `TryInto<u64>` to be useful.
339
    value: T,
340
}
341

            
342
impl<T> IntegerMilliseconds<T> {
343
    /// Public Constructor
344
103235
    pub fn new(value: T) -> Self {
345
103235
        IntegerMilliseconds { value }
346
103235
    }
347

            
348
    /// Deconstructor
349
    ///
350
    /// Use only in contexts where it's no longer possible to
351
    /// use the Rust type system to ensure secs vs ms vs us correctness.
352
5324
    pub fn as_millis(self) -> T {
353
5324
        self.value
354
5324
    }
355

            
356
    /// Map the inner value (useful for conversion)
357
    ///
358
    /// # Example
359
    ///
360
    /// ```
361
    /// use tor_units::{BoundedInt32, IntegerMilliseconds};
362
    ///
363
    /// let value: IntegerMilliseconds<i32> = 42.into();
364
    /// let value: IntegerMilliseconds<BoundedInt32<0,1000>>
365
    ///     = value.try_map(TryInto::try_into).unwrap();
366
    /// ```
367
5884
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMilliseconds<U>, E>
368
5884
    where
369
5884
        F: FnOnce(T) -> Result<U, E>,
370
    {
371
5884
        Ok(IntegerMilliseconds::new(f(self.value)?))
372
5884
    }
373
}
374

            
375
impl<T: TryInto<u64>> TryFrom<IntegerMilliseconds<T>> for Duration {
376
    type Error = <T as TryInto<u64>>::Error;
377
32
    fn try_from(val: IntegerMilliseconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
378
32
        Ok(Self::from_millis(val.value.try_into()?))
379
32
    }
380
}
381

            
382
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMilliseconds<BoundedInt32<H, L>> {
383
    type Error = Error;
384
96513
    fn try_from(v: i32) -> Result<Self, Error> {
385
96513
        Ok(IntegerMilliseconds::new(v.try_into()?))
386
96513
    }
387
}
388

            
389
#[derive(
390
    Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
391
)]
392
/// This type represents an integer number of seconds.
393
///
394
/// The underlying type should usually implement `TryInto<u64>`.
395
pub struct IntegerSeconds<T> {
396
    /// Interior Value. Should implement `TryInto<u64>` to be useful.
397
    value: T,
398
}
399

            
400
impl<T> IntegerSeconds<T> {
401
    /// Public Constructor
402
179339
    pub fn new(value: T) -> Self {
403
179339
        IntegerSeconds { value }
404
179339
    }
405

            
406
    /// Deconstructor
407
    ///
408
    /// Use only in contexts where it's no longer possible to
409
    /// use the Rust type system to ensure secs vs ms vs us correctness.
410
    pub fn as_secs(self) -> T {
411
        self.value
412
    }
413

            
414
    /// Map the inner value (useful for conversion)
415
    ///
416
    /// ```
417
    /// use tor_units::{BoundedInt32, IntegerSeconds};
418
    ///
419
    /// let value: IntegerSeconds<i32> = 42.into();
420
    /// let value: IntegerSeconds<BoundedInt32<0,1000>>
421
    ///     = value.try_map(TryInto::try_into).unwrap();
422
    /// ```
423
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerSeconds<U>, E>
424
    where
425
        F: FnOnce(T) -> Result<U, E>,
426
    {
427
        Ok(IntegerSeconds::new(f(self.value)?))
428
    }
429
}
430

            
431
impl<T: TryInto<u64>> TryFrom<IntegerSeconds<T>> for Duration {
432
    type Error = <T as TryInto<u64>>::Error;
433
17003
    fn try_from(val: IntegerSeconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
434
17003
        Ok(Self::from_secs(val.value.try_into()?))
435
17003
    }
436
}
437

            
438
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerSeconds<BoundedInt32<H, L>> {
439
    type Error = Error;
440
179235
    fn try_from(v: i32) -> Result<Self, Error> {
441
179235
        Ok(IntegerSeconds::new(v.try_into()?))
442
179235
    }
443
}
444

            
445
#[derive(Deserialize, Serialize)] //
446
#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
447
/// This type represents an integer number of minutes.
448
///
449
/// The underlying type should usually implement `TryInto<u64>`.
450
pub struct IntegerMinutes<T> {
451
    /// Interior Value. Should Implement `TryInto<u64>` to be useful.
452
    value: T,
453
}
454

            
455
impl<T> IntegerMinutes<T> {
456
    /// Public Constructor
457
40393
    pub fn new(value: T) -> Self {
458
40393
        IntegerMinutes { value }
459
40393
    }
460

            
461
    /// Deconstructor
462
    ///
463
    /// Use only in contexts where it's no longer possible to
464
    /// use the Rust type system to ensure secs vs ms vs us correctness.
465
252756
    pub fn as_minutes(self) -> T {
466
252756
        self.value
467
252756
    }
468

            
469
    /// Map the inner value (useful for conversion)
470
    ///
471
    /// ```
472
    /// use tor_units::{BoundedInt32, IntegerMinutes};
473
    ///
474
    /// let value: IntegerMinutes<i32> = 42.into();
475
    /// let value: IntegerMinutes<BoundedInt32<0,1000>>
476
    ///     = value.try_map(TryInto::try_into).unwrap();
477
    /// ```
478
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMinutes<U>, E>
479
    where
480
        F: FnOnce(T) -> Result<U, E>,
481
    {
482
        Ok(IntegerMinutes::new(f(self.value)?))
483
    }
484
}
485

            
486
impl<T: TryInto<u64>> TryFrom<IntegerMinutes<T>> for Duration {
487
    type Error = Error;
488
17810
    fn try_from(val: IntegerMinutes<T>) -> Result<Self, Error> {
489
        /// Number of seconds in a single minute.
490
        const SECONDS_PER_MINUTE: u64 = 60;
491
17810
        let minutes: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
492
17808
        let seconds = minutes
493
17808
            .checked_mul(SECONDS_PER_MINUTE)
494
17808
            .ok_or(Error::Overflow)?;
495
17806
        Ok(Self::from_secs(seconds))
496
17810
    }
497
}
498

            
499
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMinutes<BoundedInt32<H, L>> {
500
    type Error = Error;
501
13789
    fn try_from(v: i32) -> Result<Self, Error> {
502
13789
        Ok(IntegerMinutes::new(v.try_into()?))
503
13789
    }
504
}
505

            
506
#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
507
/// This type represents an integer number of days.
508
///
509
/// The underlying type should usually implement `TryInto<u64>`.
510
pub struct IntegerDays<T> {
511
    /// Interior Value. Should Implement `TryInto<u64>` to be useful.
512
    value: T,
513
}
514

            
515
impl<T> IntegerDays<T> {
516
    /// Public Constructor
517
41377
    pub fn new(value: T) -> Self {
518
41377
        IntegerDays { value }
519
41377
    }
520

            
521
    /// Deconstructor
522
    ///
523
    /// Use only in contexts where it's no longer possible to
524
    /// use the Rust type system to ensure secs vs ms vs us correctness.
525
    pub fn as_days(self) -> T {
526
        self.value
527
    }
528

            
529
    /// Map the inner value (useful for conversion)
530
    ///
531
    /// ```
532
    /// use tor_units::{BoundedInt32, IntegerDays};
533
    ///
534
    /// let value: IntegerDays<i32> = 42.into();
535
    /// let value: IntegerDays<BoundedInt32<0,1000>>
536
    ///     = value.try_map(TryInto::try_into).unwrap();
537
    /// ```
538
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerDays<U>, E>
539
    where
540
        F: FnOnce(T) -> Result<U, E>,
541
    {
542
        Ok(IntegerDays::new(f(self.value)?))
543
    }
544
}
545

            
546
impl<T: TryInto<u64>> TryFrom<IntegerDays<T>> for Duration {
547
    type Error = Error;
548
5493
    fn try_from(val: IntegerDays<T>) -> Result<Self, Error> {
549
        /// Number of seconds in a single day.
550
        const SECONDS_PER_DAY: u64 = 86400;
551
5493
        let days: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
552
5491
        let seconds = days.checked_mul(SECONDS_PER_DAY).ok_or(Error::Overflow)?;
553
5489
        Ok(Self::from_secs(seconds))
554
5493
    }
555
}
556

            
557
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerDays<BoundedInt32<H, L>> {
558
    type Error = Error;
559
41363
    fn try_from(v: i32) -> Result<Self, Error> {
560
41363
        Ok(IntegerDays::new(v.try_into()?))
561
41363
    }
562
}
563

            
564
/// A SendMe Version
565
///
566
/// DOCDOC: Explain why this needs to have its own type, or remove it.
567
#[derive(Clone, Copy, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
568
pub struct SendMeVersion(u8);
569

            
570
impl SendMeVersion {
571
    /// Public Constructor
572
31146
    pub fn new(value: u8) -> Self {
573
31146
        SendMeVersion(value)
574
31146
    }
575

            
576
    /// Helper
577
234
    pub fn get(&self) -> u8 {
578
234
        self.0
579
234
    }
580
}
581

            
582
impl TryFrom<i32> for SendMeVersion {
583
    type Error = Error;
584
30914
    fn try_from(v: i32) -> Result<Self, Error> {
585
30914
        let val_u8 = BoundedInt32::<0, 255>::checked_new(v)?;
586
30914
        Ok(SendMeVersion::new(val_u8.get() as u8))
587
30914
    }
588
}
589

            
590
/// Tests that check whether some code fails to compile as intended.
591
// Unfortunately we can't check the reason that it fails to compile,
592
// so these tests could become stale if the API is changed.
593
// In the future, we may be able to use the (currently nightly):
594
// https://doc.rust-lang.org/rustdoc/unstable-features.html?highlight=compile_fail#error-numbers-for-compile-fail-doctests
595
#[cfg(doc)]
596
#[doc(hidden)]
597
mod compile_fail_tests {
598
    /// ```compile_fail
599
    /// use tor_units::BoundedInt32;
600
    /// let _: BoundedInt32<10, 5> = BoundedInt32::saturating_new(7);
601
    /// ```
602
    fn uninhabited_saturating_new() {}
603

            
604
    /// ```compile_fail
605
    /// use tor_units::BoundedInt32;
606
    /// let _: Result<BoundedInt32<10, 5>, Error> = BoundedInt32::saturating_from_str("7");
607
    /// ```
608
    fn uninhabited_from_string() {}
609
}
610

            
611
#[cfg(test)]
612
mod tests {
613
    #![allow(clippy::unwrap_used)]
614
    use float_cmp::assert_approx_eq;
615

            
616
    use super::*;
617

            
618
    type TestFoo = BoundedInt32<1, 5>;
619
    type TestBar = BoundedInt32<-45, 17>;
620

            
621
    //make_parameter_type! {TestFoo(3,)}
622
    #[test]
623
    fn entire_range_parsed() {
624
        let x: TestFoo = "1".parse().unwrap();
625
        assert!(x.get() == 1);
626
        let x: TestFoo = "2".parse().unwrap();
627
        assert!(x.get() == 2);
628
        let x: TestFoo = "3".parse().unwrap();
629
        assert!(x.get() == 3);
630
        let x: TestFoo = "4".parse().unwrap();
631
        assert!(x.get() == 4);
632
        let x: TestFoo = "5".parse().unwrap();
633
        assert!(x.get() == 5);
634
    }
635

            
636
    #[test]
637
    fn saturating() {
638
        let x: TestFoo = TestFoo::saturating_new(1000);
639
        let x_val: i32 = x.into();
640
        assert!(x_val == TestFoo::UPPER);
641
        let x: TestFoo = TestFoo::saturating_new(0);
642
        let x_val: i32 = x.into();
643
        assert!(x_val == TestFoo::LOWER);
644
    }
645
    #[test]
646
    fn saturating_string() {
647
        let x: TestFoo = TestFoo::saturating_from_str("1000").unwrap();
648
        let x_val: i32 = x.into();
649
        assert!(x_val == TestFoo::UPPER);
650
        let x: TestFoo = TestFoo::saturating_from_str("0").unwrap();
651
        let x_val: i32 = x.into();
652
        assert!(x_val == TestFoo::LOWER);
653
    }
654

            
655
    #[test]
656
    fn errors_correct() {
657
        let x: Result<TestBar, Error> = "1000".parse();
658
        assert!(x.unwrap_err() == Error::AboveUpperBound(1000, TestBar::UPPER));
659
        let x: Result<TestBar, Error> = "-1000".parse();
660
        assert!(x.unwrap_err() == Error::BelowLowerBound(-1000, TestBar::LOWER));
661
        let x: Result<TestBar, Error> = "xyz".parse();
662
        assert!(x.unwrap_err() == Error::Unrepresentable);
663
    }
664

            
665
    #[test]
666
    fn display() {
667
        let v = BoundedInt32::<99, 1000>::checked_new(345).unwrap();
668
        assert_eq!(v.to_string(), "345".to_string());
669
    }
670

            
671
    #[test]
672
    #[should_panic]
673
    fn checked_too_high() {
674
        let _: TestBar = "1000".parse().unwrap();
675
    }
676

            
677
    #[test]
678
    #[should_panic]
679
    fn checked_too_low() {
680
        let _: TestBar = "-46".parse().unwrap();
681
    }
682

            
683
    #[test]
684
    fn bounded_to_u64() {
685
        let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
686
        let u: u64 = b.try_into().unwrap();
687
        assert_eq!(u, 77);
688

            
689
        let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(-77).unwrap();
690
        let u: Result<u64, Error> = b.try_into();
691
        assert!(u.is_err());
692
    }
693

            
694
    #[test]
695
    fn bounded_to_f64() {
696
        let x: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
697
        let f: f64 = x.into();
698
        assert_approx_eq!(f64, f, 77.0);
699
    }
700

            
701
    #[test]
702
    fn bounded_from_i32() {
703
        let x: Result<BoundedInt32<-100, 100>, _> = 50.try_into();
704
        let y: i32 = x.unwrap().into();
705
        assert_eq!(y, 50);
706

            
707
        let x: Result<BoundedInt32<-100, 100>, _> = 1000.try_into();
708
        assert!(x.is_err());
709
    }
710

            
711
    #[test]
712
    fn into_bool() {
713
        let zero: BoundedInt32<0, 1> = BoundedInt32::saturating_from(0);
714
        let one: BoundedInt32<0, 1> = BoundedInt32::saturating_from(1);
715

            
716
        let f: bool = zero.into();
717
        let t: bool = one.into();
718
        assert!(!f);
719
        assert!(t);
720
    }
721

            
722
    #[test]
723
    fn into_u8() {
724
        let zero: BoundedInt32<0, 255> = BoundedInt32::saturating_from(0);
725
        let one: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1);
726
        let ninety: BoundedInt32<0, 255> = BoundedInt32::saturating_from(90);
727
        let max: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1000);
728

            
729
        let a: u8 = zero.into();
730
        let b: u8 = one.into();
731
        let c: u8 = ninety.into();
732
        let d: u8 = max.into();
733

            
734
        assert_eq!(a, 0);
735
        assert_eq!(b, 1);
736
        assert_eq!(c, 90);
737
        assert_eq!(d, 255);
738
    }
739

            
740
    #[test]
741
    fn into_u32() {
742
        let zero: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(0);
743
        let one: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1);
744
        let ninety: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(90);
745
        let max: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1000);
746

            
747
        assert_eq!(u32::from(zero), 0);
748
        assert_eq!(u32::from(one), 1);
749
        assert_eq!(u32::from(ninety), 90);
750
        assert_eq!(u32::from(max), 1000);
751

            
752
        let zero: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(0);
753
        let one: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1);
754
        let ninety: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(90);
755
        let max: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1000);
756

            
757
        assert_eq!(u32::from(zero), 1);
758
        assert_eq!(u32::from(one), 1);
759
        assert_eq!(u32::from(ninety), 90);
760
        assert_eq!(u32::from(max), 1000);
761
    }
762

            
763
    #[test]
764
    fn try_into_usize() {
765
        let b0: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(0);
766
        let b100: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(100);
767
        let bn5: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(-5);
768
        assert_eq!(usize::try_from(b0), Ok(0_usize));
769
        assert_eq!(usize::try_from(b100), Ok(100_usize));
770
        assert_eq!(usize::try_from(bn5), Err(Error::Negative));
771
    }
772

            
773
    #[test]
774
    fn percents() {
775
        type Pct = Percentage<u8>;
776
        let p = Pct::new(100);
777
        assert_eq!(p.as_percent(), 100);
778
        assert_approx_eq!(f64, p.as_fraction(), 1.0);
779

            
780
        let p = Pct::new(0);
781
        assert_eq!(p.as_percent(), 0);
782
        assert_approx_eq!(f64, p.as_fraction(), 0.0);
783

            
784
        let p = Pct::new(25);
785
        assert_eq!(p.as_percent(), 25);
786
        assert_eq!(p.clone(), p);
787
        assert_approx_eq!(f64, p.as_fraction(), 0.25);
788

            
789
        type BPct = Percentage<BoundedInt32<0, 100>>;
790
        assert_eq!(BPct::try_from(99).unwrap().as_percent().get(), 99);
791
    }
792

            
793
    #[test]
794
    fn milliseconds() {
795
        type Msec = IntegerMilliseconds<i32>;
796

            
797
        let ms = Msec::new(500);
798
        let d: Result<Duration, _> = ms.try_into();
799
        assert_eq!(d.unwrap(), Duration::from_millis(500));
800
        assert_eq!(Duration::try_from(ms * 2).unwrap(), Duration::from_secs(1));
801

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

            
806
        type BMSec = IntegerMilliseconds<BoundedInt32<0, 1000>>;
807
        let half_sec = BMSec::try_from(500).unwrap();
808
        assert_eq!(
809
            Duration::try_from(half_sec).unwrap(),
810
            Duration::from_millis(500)
811
        );
812
        assert!(BMSec::try_from(1001).is_err());
813
    }
814

            
815
    #[test]
816
    fn seconds() {
817
        type Sec = IntegerSeconds<i32>;
818

            
819
        let ms = Sec::new(500);
820
        let d: Result<Duration, _> = ms.try_into();
821
        assert_eq!(d.unwrap(), Duration::from_secs(500));
822

            
823
        let ms = Sec::new(-100);
824
        let d: Result<Duration, _> = ms.try_into();
825
        assert!(d.is_err());
826

            
827
        type BSec = IntegerSeconds<BoundedInt32<0, 3600>>;
828
        let half_hour = BSec::try_from(1800).unwrap();
829
        assert_eq!(
830
            Duration::try_from(half_hour).unwrap(),
831
            Duration::from_secs(1800)
832
        );
833
        assert!(BSec::try_from(9999).is_err());
834
        assert_eq!(half_hour.clone(), half_hour);
835
    }
836

            
837
    #[test]
838
    fn minutes() {
839
        type Min = IntegerMinutes<i32>;
840

            
841
        let t = Min::new(500);
842
        let d: Duration = t.try_into().unwrap();
843
        assert_eq!(d, Duration::from_secs(500 * 60));
844

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

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

            
853
        type BMin = IntegerMinutes<BoundedInt32<10, 30>>;
854
        assert_eq!(
855
            BMin::new(17_i32.try_into().unwrap()),
856
            BMin::try_from(17).unwrap()
857
        );
858
    }
859

            
860
    #[test]
861
    fn days() {
862
        type Days = IntegerDays<i32>;
863

            
864
        let t = Days::new(500);
865
        let d: Duration = t.try_into().unwrap();
866
        assert_eq!(d, Duration::from_secs(500 * 86400));
867

            
868
        let t = Days::new(-100);
869
        let d: Result<Duration, _> = t.try_into();
870
        assert_eq!(d, Err(Error::Overflow));
871

            
872
        let t = IntegerDays::<u64>::new(u64::MAX);
873
        let d: Result<Duration, _> = t.try_into();
874
        assert_eq!(d, Err(Error::Overflow));
875

            
876
        type BDays = IntegerDays<BoundedInt32<10, 30>>;
877
        assert_eq!(
878
            BDays::new(17_i32.try_into().unwrap()),
879
            BDays::try_from(17).unwrap()
880
        );
881
    }
882

            
883
    #[test]
884
    fn sendme() {
885
        let smv = SendMeVersion::new(5);
886
        assert_eq!(smv.get(), 5);
887
        assert_eq!(smv.clone().get(), 5);
888
        assert_eq!(smv, SendMeVersion::try_from(5).unwrap());
889
    }
890
}