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
#![deny(clippy::string_slice)] // See arti#2571
48
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
49

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

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

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

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

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

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

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

            
120
1774165
        BoundedInt32 { value }
121
1774165
    }
122

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
625
    use super::*;
626

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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