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
#![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_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
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
48

            
49
use derive_more::{Add, Display, Div, From, FromStr, Mul};
50

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

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

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

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

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

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

            
119
1699532
        BoundedInt32 { value }
120
1699532
    }
121

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
344
impl<T> IntegerMilliseconds<T> {
345
    /// Public Constructor
346
125304
    pub fn new(value: T) -> Self {
347
125304
        IntegerMilliseconds { value }
348
125304
    }
349

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

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

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

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

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

            
402
impl<T> IntegerSeconds<T> {
403
    /// Public Constructor
404
213546
    pub fn new(value: T) -> Self {
405
213546
        IntegerSeconds { value }
406
213546
    }
407

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

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

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

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

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

            
457
impl<T> IntegerMinutes<T> {
458
    /// Public Constructor
459
45613
    pub fn new(value: T) -> Self {
460
45613
        IntegerMinutes { value }
461
45613
    }
462

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

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

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

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

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

            
517
impl<T> IntegerDays<T> {
518
    /// Public Constructor
519
49270
    pub fn new(value: T) -> Self {
520
49270
        IntegerDays { value }
521
49270
    }
522

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

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

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

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

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

            
572
impl SendMeVersion {
573
    /// Public Constructor
574
36628
    pub fn new(value: u8) -> Self {
575
36628
        SendMeVersion(value)
576
36628
    }
577

            
578
    /// Helper
579
244
    pub fn get(&self) -> u8 {
580
244
        self.0
581
244
    }
582
}
583

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

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

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

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

            
618
    use super::*;
619

            
620
    type TestFoo = BoundedInt32<1, 5>;
621
    type TestBar = BoundedInt32<-45, 17>;
622

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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