1
#![cfg_attr(docsrs, feature(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
#![allow(clippy::cognitive_complexity)] // See arti#2556
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_time_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
#![allow(clippy::collapsible_if)] // See arti#2342
46
#![deny(clippy::unused_async)]
47
#![deny(clippy::string_slice)] // See arti#2571
48
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
49
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
50

            
51
use derive_more::{Add, Display, Div, From, FromStr, Mul};
52

            
53
use serde::{Deserialize, Serialize};
54
use std::time::Duration;
55
use thiserror::Error;
56

            
57
#[cfg(feature = "memquota-memcost")]
58
use {derive_deftly::Deftly, tor_memquota::derive_deftly_template_HasMemoryCost};
59

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

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

            
109
impl<const LOWER: i32, const UPPER: i32> BoundedInt32<LOWER, UPPER> {
110
    /// Lower bound
111
    pub const LOWER: i32 = LOWER;
112
    /// Upper bound
113
    pub const UPPER: i32 = UPPER;
114

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

            
121
1779473
        BoundedInt32 { value }
122
1779473
    }
123

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

            
131
    /// Return the lower bound value of this bounded i32.
132
    ///
133
    /// This always return [`Self::LOWER`].
134
110
    pub const fn upper(&self) -> i32 {
135
110
        UPPER
136
110
    }
137

            
138
    /// Return the underlying i32 value.
139
    ///
140
    /// This value will always be between [`Self::LOWER`] and [`Self::UPPER`],
141
    /// inclusive.
142
47306
    pub fn get(&self) -> i32 {
143
47306
        self.value
144
47306
    }
145

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

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

            
164
    /// If `val` is an acceptable value inside the range for this type,
165
    /// return a new [`BoundedInt32`].  Otherwise return an error.
166
1779445
    pub fn checked_new(val: i32) -> Result<Self, Error> {
167
1779445
        if val > UPPER {
168
64
            Err(Error::AboveUpperBound(val, UPPER))
169
1779381
        } else if val < LOWER {
170
4
            Err(Error::BelowLowerBound(val, LOWER))
171
        } else {
172
1779377
            Ok(BoundedInt32::unchecked_new(val))
173
        }
174
1779445
    }
175

            
176
    /// This private function clamps an input to the acceptable range.
177
96
    fn clamp(val: i32) -> i32 {
178
96
        Ord::clamp(val, LOWER, UPPER)
179
96
    }
180

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

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

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

            
209
impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for i32 {
210
10
    fn from(val: BoundedInt32<L, U>) -> i32 {
211
10
        val.value
212
10
    }
213
}
214

            
215
impl<const L: i32, const U: i32> From<BoundedInt32<L, U>> for f64 {
216
16018
    fn from(val: BoundedInt32<L, U>) -> f64 {
217
16018
        val.value.into()
218
16018
    }
219
}
220

            
221
impl<const L: i32, const H: i32> TryFrom<i32> for BoundedInt32<L, H> {
222
    type Error = Error;
223
1671822
    fn try_from(val: i32) -> Result<Self, Self::Error> {
224
1671822
        Self::checked_new(val)
225
1671822
    }
226
}
227

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

            
235
impl From<BoundedInt32<0, 1>> for bool {
236
1212068
    fn from(val: BoundedInt32<0, 1>) -> bool {
237
1212068
        val.value == 1
238
1212068
    }
239
}
240

            
241
impl From<BoundedInt32<0, 255>> for u8 {
242
8
    fn from(val: BoundedInt32<0, 255>) -> u8 {
243
8
        val.value as u8
244
8
    }
245
}
246

            
247
impl From<BoundedInt32<1, 254>> for u8 {
248
4116
    fn from(val: BoundedInt32<1, 254>) -> u8 {
249
4116
        val.value as u8
250
4116
    }
251
}
252

            
253
impl<const L: i32, const H: i32> From<BoundedInt32<L, H>> for u32 {
254
20038
    fn from(val: BoundedInt32<L, H>) -> u32 {
255
20038
        val.value as u32
256
20038
    }
257
}
258

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

            
270
impl<const L: i32, const H: i32> TryFrom<BoundedInt32<L, H>> for usize {
271
    type Error = Error;
272
15470
    fn try_from(val: BoundedInt32<L, H>) -> Result<Self, Self::Error> {
273
15470
        if val.value < 0 {
274
2
            Err(Error::Negative)
275
        } else {
276
15468
            Ok(val.value as usize)
277
        }
278
15470
    }
279
}
280

            
281
/// A percentage value represented as a number.
282
///
283
/// This type wraps an underlying numeric type, and ensures that callers
284
/// are clear whether they want a _fraction_, or a _percentage_.
285
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
286
pub struct Percentage<T: Copy + Into<f64>> {
287
    /// The underlying percentage value.
288
    value: T,
289
}
290

            
291
impl<T: Copy + Into<f64>> Percentage<T> {
292
    /// Create a new `IntPercentage` from the underlying percentage.
293
187436
    pub fn new(value: T) -> Self {
294
187436
        Self { value }
295
187436
    }
296

            
297
    /// Return this value as a (possibly improper) fraction.
298
    ///
299
    /// ```
300
    /// use tor_units::Percentage;
301
    /// let pct_200 = Percentage::<u8>::new(200);
302
    /// let pct_100 = Percentage::<u8>::new(100);
303
    /// let pct_50 = Percentage::<u8>::new(50);
304
    ///
305
    /// assert_eq!(pct_200.as_fraction(), 2.0);
306
    /// assert_eq!(pct_100.as_fraction(), 1.0);
307
    /// assert_eq!(pct_50.as_fraction(), 0.5);
308
    /// // Note: don't actually compare f64 with ==.
309
    /// ```
310
16022
    pub fn as_fraction(self) -> f64 {
311
16022
        self.value.into() / 100.0
312
16022
    }
313

            
314
    /// Return this value as a percentage.
315
    ///
316
    /// ```
317
    /// use tor_units::Percentage;
318
    /// let pct_200 = Percentage::<u8>::new(200);
319
    /// let pct_100 = Percentage::<u8>::new(100);
320
    /// let pct_50 = Percentage::<u8>::new(50);
321
    ///
322
    /// assert_eq!(pct_200.as_percent(), 200);
323
    /// assert_eq!(pct_100.as_percent(), 100);
324
    /// assert_eq!(pct_50.as_percent(), 50);
325
    /// ```
326
762
    pub fn as_percent(self) -> T {
327
762
        self.value
328
762
    }
329
}
330

            
331
impl<const H: i32, const L: i32> TryFrom<i32> for Percentage<BoundedInt32<H, L>> {
332
    type Error = Error;
333
183900
    fn try_from(v: i32) -> Result<Self, Error> {
334
183900
        Ok(Percentage::new(v.try_into()?))
335
183900
    }
336
}
337

            
338
// TODO: There is a bunch of code duplication among these "IntegerTimeUnits"
339
// section.
340

            
341
#[derive(
342
    Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
343
)]
344
/// This type represents an integer number of milliseconds.
345
///
346
/// The underlying type should usually implement `TryInto<u64>`.
347
pub struct IntegerMilliseconds<T> {
348
    /// Interior Value. Should implement `TryInto<u64>` to be useful.
349
    value: T,
350
}
351

            
352
impl<T> IntegerMilliseconds<T> {
353
    /// Public Constructor
354
123678
    pub fn new(value: T) -> Self {
355
123678
        IntegerMilliseconds { value }
356
123678
    }
357

            
358
    /// Deconstructor
359
    ///
360
    /// Use only in contexts where it's no longer possible to
361
    /// use the Rust type system to ensure secs vs ms vs us correctness.
362
10184
    pub fn as_millis(self) -> T {
363
10184
        self.value
364
10184
    }
365

            
366
    /// Map the inner value (useful for conversion)
367
    ///
368
    /// # Example
369
    ///
370
    /// ```
371
    /// use tor_units::{BoundedInt32, IntegerMilliseconds};
372
    ///
373
    /// let value: IntegerMilliseconds<i32> = 42.into();
374
    /// let value: IntegerMilliseconds<BoundedInt32<0,1000>>
375
    ///     = value.try_map(TryInto::try_into).unwrap();
376
    /// ```
377
3836
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMilliseconds<U>, E>
378
3836
    where
379
3836
        F: FnOnce(T) -> Result<U, E>,
380
    {
381
3836
        Ok(IntegerMilliseconds::new(f(self.value)?))
382
3836
    }
383
}
384

            
385
impl<T: TryInto<u64>> TryFrom<IntegerMilliseconds<T>> for Duration {
386
    type Error = <T as TryInto<u64>>::Error;
387
32
    fn try_from(val: IntegerMilliseconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
388
32
        Ok(Self::from_millis(val.value.try_into()?))
389
32
    }
390
}
391

            
392
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMilliseconds<BoundedInt32<H, L>> {
393
    type Error = Error;
394
117030
    fn try_from(v: i32) -> Result<Self, Error> {
395
117030
        Ok(IntegerMilliseconds::new(v.try_into()?))
396
117030
    }
397
}
398

            
399
#[derive(
400
    Add, Copy, Clone, Mul, Div, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash,
401
)]
402
/// This type represents an integer number of seconds.
403
///
404
/// The underlying type should usually implement `TryInto<u64>`.
405
pub struct IntegerSeconds<T> {
406
    /// Interior Value. Should implement `TryInto<u64>` to be useful.
407
    value: T,
408
}
409

            
410
impl<T> IntegerSeconds<T> {
411
    /// Public Constructor
412
217448
    pub fn new(value: T) -> Self {
413
217448
        IntegerSeconds { value }
414
217448
    }
415

            
416
    /// Deconstructor
417
    ///
418
    /// Use only in contexts where it's no longer possible to
419
    /// use the Rust type system to ensure secs vs ms vs us correctness.
420
    pub fn as_secs(self) -> T {
421
        self.value
422
    }
423

            
424
    /// Map the inner value (useful for conversion)
425
    ///
426
    /// ```
427
    /// use tor_units::{BoundedInt32, IntegerSeconds};
428
    ///
429
    /// let value: IntegerSeconds<i32> = 42.into();
430
    /// let value: IntegerSeconds<BoundedInt32<0,1000>>
431
    ///     = value.try_map(TryInto::try_into).unwrap();
432
    /// ```
433
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerSeconds<U>, E>
434
    where
435
        F: FnOnce(T) -> Result<U, E>,
436
    {
437
        Ok(IntegerSeconds::new(f(self.value)?))
438
    }
439
}
440

            
441
impl<T: TryInto<u64>> TryFrom<IntegerSeconds<T>> for Duration {
442
    type Error = <T as TryInto<u64>>::Error;
443
17810
    fn try_from(val: IntegerSeconds<T>) -> Result<Self, <T as TryInto<u64>>::Error> {
444
17810
        Ok(Self::from_secs(val.value.try_into()?))
445
17810
    }
446
}
447

            
448
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerSeconds<BoundedInt32<H, L>> {
449
    type Error = Error;
450
217338
    fn try_from(v: i32) -> Result<Self, Error> {
451
217338
        Ok(IntegerSeconds::new(v.try_into()?))
452
217338
    }
453
}
454

            
455
#[derive(Deserialize, Serialize)] //
456
#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
457
/// This type represents an integer number of minutes.
458
///
459
/// The underlying type should usually implement `TryInto<u64>`.
460
pub struct IntegerMinutes<T> {
461
    /// Interior Value. Should Implement `TryInto<u64>` to be useful.
462
    value: T,
463
}
464

            
465
impl<T> IntegerMinutes<T> {
466
    /// Public Constructor
467
47765
    pub fn new(value: T) -> Self {
468
47765
        IntegerMinutes { value }
469
47765
    }
470

            
471
    /// Deconstructor
472
    ///
473
    /// Use only in contexts where it's no longer possible to
474
    /// use the Rust type system to ensure secs vs ms vs us correctness.
475
297650
    pub fn as_minutes(self) -> T {
476
297650
        self.value
477
297650
    }
478

            
479
    /// Map the inner value (useful for conversion)
480
    ///
481
    /// ```
482
    /// use tor_units::{BoundedInt32, IntegerMinutes};
483
    ///
484
    /// let value: IntegerMinutes<i32> = 42.into();
485
    /// let value: IntegerMinutes<BoundedInt32<0,1000>>
486
    ///     = value.try_map(TryInto::try_into).unwrap();
487
    /// ```
488
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerMinutes<U>, E>
489
    where
490
        F: FnOnce(T) -> Result<U, E>,
491
    {
492
        Ok(IntegerMinutes::new(f(self.value)?))
493
    }
494
}
495

            
496
impl<T: TryInto<u64>> TryFrom<IntegerMinutes<T>> for Duration {
497
    type Error = Error;
498
19952
    fn try_from(val: IntegerMinutes<T>) -> Result<Self, Error> {
499
        /// Number of seconds in a single minute.
500
        const SECONDS_PER_MINUTE: u64 = 60;
501
19952
        let minutes: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
502
19950
        let seconds = minutes
503
19950
            .checked_mul(SECONDS_PER_MINUTE)
504
19950
            .ok_or(Error::Overflow)?;
505
19948
        Ok(Self::from_secs(seconds))
506
19952
    }
507
}
508

            
509
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerMinutes<BoundedInt32<H, L>> {
510
    type Error = Error;
511
16720
    fn try_from(v: i32) -> Result<Self, Error> {
512
16720
        Ok(IntegerMinutes::new(v.try_into()?))
513
16720
    }
514
}
515

            
516
#[derive(Copy, Clone, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
517
/// This type represents an integer number of days.
518
///
519
/// The underlying type should usually implement `TryInto<u64>`.
520
pub struct IntegerDays<T> {
521
    /// Interior Value. Should Implement `TryInto<u64>` to be useful.
522
    value: T,
523
}
524

            
525
impl<T> IntegerDays<T> {
526
    /// Public Constructor
527
50170
    pub fn new(value: T) -> Self {
528
50170
        IntegerDays { value }
529
50170
    }
530

            
531
    /// Deconstructor
532
    ///
533
    /// Use only in contexts where it's no longer possible to
534
    /// use the Rust type system to ensure secs vs ms vs us correctness.
535
    pub fn as_days(self) -> T {
536
        self.value
537
    }
538

            
539
    /// Map the inner value (useful for conversion)
540
    ///
541
    /// ```
542
    /// use tor_units::{BoundedInt32, IntegerDays};
543
    ///
544
    /// let value: IntegerDays<i32> = 42.into();
545
    /// let value: IntegerDays<BoundedInt32<0,1000>>
546
    ///     = value.try_map(TryInto::try_into).unwrap();
547
    /// ```
548
    pub fn try_map<U, F, E>(self, f: F) -> Result<IntegerDays<U>, E>
549
    where
550
        F: FnOnce(T) -> Result<U, E>,
551
    {
552
        Ok(IntegerDays::new(f(self.value)?))
553
    }
554
}
555

            
556
impl<T: TryInto<u64>> TryFrom<IntegerDays<T>> for Duration {
557
    type Error = Error;
558
5868
    fn try_from(val: IntegerDays<T>) -> Result<Self, Error> {
559
        /// Number of seconds in a single day.
560
        const SECONDS_PER_DAY: u64 = 86400;
561
5868
        let days: u64 = val.value.try_into().map_err(|_| Error::Overflow)?;
562
5866
        let seconds = days.checked_mul(SECONDS_PER_DAY).ok_or(Error::Overflow)?;
563
5864
        Ok(Self::from_secs(seconds))
564
5868
    }
565
}
566

            
567
impl<const H: i32, const L: i32> TryFrom<i32> for IntegerDays<BoundedInt32<H, L>> {
568
    type Error = Error;
569
50156
    fn try_from(v: i32) -> Result<Self, Error> {
570
50156
        Ok(IntegerDays::new(v.try_into()?))
571
50156
    }
572
}
573

            
574
/// A SendMe Version
575
///
576
/// DOCDOC: Explain why this needs to have its own type, or remove it.
577
#[derive(Clone, Copy, From, FromStr, Display, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
578
pub struct SendMeVersion(u8);
579

            
580
impl SendMeVersion {
581
    /// Public Constructor
582
37293
    pub fn new(value: u8) -> Self {
583
37293
        SendMeVersion(value)
584
37293
    }
585

            
586
    /// Helper
587
249
    pub fn get(&self) -> u8 {
588
249
        self.0
589
249
    }
590
}
591

            
592
impl TryFrom<i32> for SendMeVersion {
593
    type Error = Error;
594
37046
    fn try_from(v: i32) -> Result<Self, Error> {
595
37046
        let val_u8 = BoundedInt32::<0, 255>::checked_new(v)?;
596
37046
        Ok(SendMeVersion::new(val_u8.get() as u8))
597
37046
    }
598
}
599

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

            
614
    /// ```compile_fail
615
    /// use tor_units::BoundedInt32;
616
    /// let _: Result<BoundedInt32<10, 5>, Error> = BoundedInt32::saturating_from_str("7");
617
    /// ```
618
    fn uninhabited_from_string() {}
619
}
620

            
621
#[cfg(test)]
622
mod tests {
623
    #![allow(clippy::unwrap_used)]
624
    use float_cmp::assert_approx_eq;
625

            
626
    use super::*;
627

            
628
    type TestFoo = BoundedInt32<1, 5>;
629
    type TestBar = BoundedInt32<-45, 17>;
630

            
631
    //make_parameter_type! {TestFoo(3,)}
632
    #[test]
633
    fn entire_range_parsed() {
634
        let x: TestFoo = "1".parse().unwrap();
635
        assert!(x.get() == 1);
636
        let x: TestFoo = "2".parse().unwrap();
637
        assert!(x.get() == 2);
638
        let x: TestFoo = "3".parse().unwrap();
639
        assert!(x.get() == 3);
640
        let x: TestFoo = "4".parse().unwrap();
641
        assert!(x.get() == 4);
642
        let x: TestFoo = "5".parse().unwrap();
643
        assert!(x.get() == 5);
644
    }
645

            
646
    #[test]
647
    fn saturating() {
648
        let x: TestFoo = TestFoo::saturating_new(1000);
649
        let x_val: i32 = x.into();
650
        assert!(x_val == TestFoo::UPPER);
651
        let x: TestFoo = TestFoo::saturating_new(0);
652
        let x_val: i32 = x.into();
653
        assert!(x_val == TestFoo::LOWER);
654
    }
655
    #[test]
656
    fn saturating_string() {
657
        let x: TestFoo = TestFoo::saturating_from_str("1000").unwrap();
658
        let x_val: i32 = x.into();
659
        assert!(x_val == TestFoo::UPPER);
660
        let x: TestFoo = TestFoo::saturating_from_str("0").unwrap();
661
        let x_val: i32 = x.into();
662
        assert!(x_val == TestFoo::LOWER);
663
    }
664

            
665
    #[test]
666
    fn errors_correct() {
667
        let x: Result<TestBar, Error> = "1000".parse();
668
        assert!(x.unwrap_err() == Error::AboveUpperBound(1000, TestBar::UPPER));
669
        let x: Result<TestBar, Error> = "-1000".parse();
670
        assert!(x.unwrap_err() == Error::BelowLowerBound(-1000, TestBar::LOWER));
671
        let x: Result<TestBar, Error> = "xyz".parse();
672
        assert!(x.unwrap_err() == Error::Unrepresentable);
673
    }
674

            
675
    #[test]
676
    fn display() {
677
        let v = BoundedInt32::<99, 1000>::checked_new(345).unwrap();
678
        assert_eq!(v.to_string(), "345".to_string());
679
    }
680

            
681
    #[test]
682
    #[should_panic]
683
    fn checked_too_high() {
684
        let _: TestBar = "1000".parse().unwrap();
685
    }
686

            
687
    #[test]
688
    #[should_panic]
689
    fn checked_too_low() {
690
        let _: TestBar = "-46".parse().unwrap();
691
    }
692

            
693
    #[test]
694
    fn bounded_to_u64() {
695
        let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
696
        let u: u64 = b.try_into().unwrap();
697
        assert_eq!(u, 77);
698

            
699
        let b: BoundedInt32<-100, 100> = BoundedInt32::checked_new(-77).unwrap();
700
        let u: Result<u64, Error> = b.try_into();
701
        assert!(u.is_err());
702
    }
703

            
704
    #[test]
705
    fn bounded_to_f64() {
706
        let x: BoundedInt32<-100, 100> = BoundedInt32::checked_new(77).unwrap();
707
        let f: f64 = x.into();
708
        assert_approx_eq!(f64, f, 77.0);
709
    }
710

            
711
    #[test]
712
    fn bounded_from_i32() {
713
        let x: Result<BoundedInt32<-100, 100>, _> = 50.try_into();
714
        let y: i32 = x.unwrap().into();
715
        assert_eq!(y, 50);
716

            
717
        let x: Result<BoundedInt32<-100, 100>, _> = 1000.try_into();
718
        assert!(x.is_err());
719
    }
720

            
721
    #[test]
722
    fn into_bool() {
723
        let zero: BoundedInt32<0, 1> = BoundedInt32::saturating_from(0);
724
        let one: BoundedInt32<0, 1> = BoundedInt32::saturating_from(1);
725

            
726
        let f: bool = zero.into();
727
        let t: bool = one.into();
728
        assert!(!f);
729
        assert!(t);
730
    }
731

            
732
    #[test]
733
    fn into_u8() {
734
        let zero: BoundedInt32<0, 255> = BoundedInt32::saturating_from(0);
735
        let one: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1);
736
        let ninety: BoundedInt32<0, 255> = BoundedInt32::saturating_from(90);
737
        let max: BoundedInt32<0, 255> = BoundedInt32::saturating_from(1000);
738

            
739
        let a: u8 = zero.into();
740
        let b: u8 = one.into();
741
        let c: u8 = ninety.into();
742
        let d: u8 = max.into();
743

            
744
        assert_eq!(a, 0);
745
        assert_eq!(b, 1);
746
        assert_eq!(c, 90);
747
        assert_eq!(d, 255);
748
    }
749

            
750
    #[test]
751
    fn into_u32() {
752
        let zero: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(0);
753
        let one: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1);
754
        let ninety: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(90);
755
        let max: BoundedInt32<0, 1000> = BoundedInt32::saturating_from(1000);
756

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

            
762
        let zero: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(0);
763
        let one: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1);
764
        let ninety: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(90);
765
        let max: BoundedInt32<1, 1000> = BoundedInt32::saturating_from(1000);
766

            
767
        assert_eq!(u32::from(zero), 1);
768
        assert_eq!(u32::from(one), 1);
769
        assert_eq!(u32::from(ninety), 90);
770
        assert_eq!(u32::from(max), 1000);
771
    }
772

            
773
    #[test]
774
    fn try_into_usize() {
775
        let b0: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(0);
776
        let b100: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(100);
777
        let bn5: BoundedInt32<-10, 300> = BoundedInt32::saturating_from(-5);
778
        assert_eq!(usize::try_from(b0), Ok(0_usize));
779
        assert_eq!(usize::try_from(b100), Ok(100_usize));
780
        assert_eq!(usize::try_from(bn5), Err(Error::Negative));
781
    }
782

            
783
    #[test]
784
    fn percents() {
785
        type Pct = Percentage<u8>;
786
        let p = Pct::new(100);
787
        assert_eq!(p.as_percent(), 100);
788
        assert_approx_eq!(f64, p.as_fraction(), 1.0);
789

            
790
        let p = Pct::new(0);
791
        assert_eq!(p.as_percent(), 0);
792
        assert_approx_eq!(f64, p.as_fraction(), 0.0);
793

            
794
        let p = Pct::new(25);
795
        assert_eq!(p.as_percent(), 25);
796
        assert_eq!(p.clone(), p);
797
        assert_approx_eq!(f64, p.as_fraction(), 0.25);
798

            
799
        type BPct = Percentage<BoundedInt32<0, 100>>;
800
        assert_eq!(BPct::try_from(99).unwrap().as_percent().get(), 99);
801
    }
802

            
803
    #[test]
804
    fn milliseconds() {
805
        type Msec = IntegerMilliseconds<i32>;
806

            
807
        let ms = Msec::new(500);
808
        let d: Result<Duration, _> = ms.try_into();
809
        assert_eq!(d.unwrap(), Duration::from_millis(500));
810
        assert_eq!(Duration::try_from(ms * 2).unwrap(), Duration::from_secs(1));
811

            
812
        let ms = Msec::new(-100);
813
        let d: Result<Duration, _> = ms.try_into();
814
        assert!(d.is_err());
815

            
816
        type BMSec = IntegerMilliseconds<BoundedInt32<0, 1000>>;
817
        let half_sec = BMSec::try_from(500).unwrap();
818
        assert_eq!(
819
            Duration::try_from(half_sec).unwrap(),
820
            Duration::from_millis(500)
821
        );
822
        assert!(BMSec::try_from(1001).is_err());
823
    }
824

            
825
    #[test]
826
    fn seconds() {
827
        type Sec = IntegerSeconds<i32>;
828

            
829
        let ms = Sec::new(500);
830
        let d: Result<Duration, _> = ms.try_into();
831
        assert_eq!(d.unwrap(), Duration::from_secs(500));
832

            
833
        let ms = Sec::new(-100);
834
        let d: Result<Duration, _> = ms.try_into();
835
        assert!(d.is_err());
836

            
837
        type BSec = IntegerSeconds<BoundedInt32<0, 3600>>;
838
        let half_hour = BSec::try_from(1800).unwrap();
839
        assert_eq!(
840
            Duration::try_from(half_hour).unwrap(),
841
            Duration::from_secs(1800)
842
        );
843
        assert!(BSec::try_from(9999).is_err());
844
        assert_eq!(half_hour.clone(), half_hour);
845
    }
846

            
847
    #[test]
848
    fn minutes() {
849
        type Min = IntegerMinutes<i32>;
850

            
851
        let t = Min::new(500);
852
        let d: Duration = t.try_into().unwrap();
853
        assert_eq!(d, Duration::from_secs(500 * 60));
854

            
855
        let t = Min::new(-100);
856
        let d: Result<Duration, _> = t.try_into();
857
        assert_eq!(d, Err(Error::Overflow));
858

            
859
        let t = IntegerMinutes::<u64>::new(u64::MAX);
860
        let d: Result<Duration, _> = t.try_into();
861
        assert_eq!(d, Err(Error::Overflow));
862

            
863
        type BMin = IntegerMinutes<BoundedInt32<10, 30>>;
864
        assert_eq!(
865
            BMin::new(17_i32.try_into().unwrap()),
866
            BMin::try_from(17).unwrap()
867
        );
868
    }
869

            
870
    #[test]
871
    fn days() {
872
        type Days = IntegerDays<i32>;
873

            
874
        let t = Days::new(500);
875
        let d: Duration = t.try_into().unwrap();
876
        assert_eq!(d, Duration::from_secs(500 * 86400));
877

            
878
        let t = Days::new(-100);
879
        let d: Result<Duration, _> = t.try_into();
880
        assert_eq!(d, Err(Error::Overflow));
881

            
882
        let t = IntegerDays::<u64>::new(u64::MAX);
883
        let d: Result<Duration, _> = t.try_into();
884
        assert_eq!(d, Err(Error::Overflow));
885

            
886
        type BDays = IntegerDays<BoundedInt32<10, 30>>;
887
        assert_eq!(
888
            BDays::new(17_i32.try_into().unwrap()),
889
            BDays::try_from(17).unwrap()
890
        );
891
    }
892

            
893
    #[test]
894
    fn sendme() {
895
        let smv = SendMeVersion::new(5);
896
        assert_eq!(smv.get(), 5);
897
        assert_eq!(smv.clone().get(), 5);
898
        assert_eq!(smv, SendMeVersion::try_from(5).unwrap());
899
    }
900
}