|
| 1 | +use std::num::{IntErrorKind, TryFromIntError}; |
| 2 | +use std::time::Duration; |
| 3 | + |
| 4 | +pub type PeriodLength = Duration; |
| 5 | +pub type PeriodCount = u64; |
| 6 | +pub type PeriodTotalTime = Duration; |
| 7 | + |
| 8 | +pub trait Periods: Sized + Default { |
| 9 | + fn new(length: &PeriodLength, count: &PeriodCount) -> Self; |
| 10 | + |
| 11 | + fn add_count(&self, add: PeriodCount) -> Result<Self, IntErrorKind>; |
| 12 | + fn sub_count(&self, sub: PeriodCount) -> Result<Self, IntErrorKind>; |
| 13 | +} |
| 14 | + |
| 15 | +pub trait PeriodsTotals: Periods { |
| 16 | + fn total_time(&self) -> Result<Option<PeriodTotalTime>, TryFromIntError>; |
| 17 | + fn total_time_end(&self) -> Result<Option<PeriodTotalTime>, TryFromIntError>; |
| 18 | +} |
| 19 | + |
| 20 | +#[derive(Debug, Default, Hash, PartialEq)] |
| 21 | +pub struct Period { |
| 22 | + pub length: PeriodLength, |
| 23 | + pub count: PeriodCount, |
| 24 | +} |
| 25 | + |
| 26 | +pub type SinceUnixEpochPeriods = Period; |
| 27 | + |
| 28 | +impl Period { |
| 29 | + pub const fn from_sec(length: u64, count: u64) -> Self { |
| 30 | + Self { |
| 31 | + length: Duration::from_secs(length), |
| 32 | + count: count, |
| 33 | + } |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +impl Periods for Period { |
| 38 | + fn new(length: &PeriodLength, count: &PeriodCount) -> Self { |
| 39 | + Self { |
| 40 | + length: *length, |
| 41 | + count: *count, |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | + fn add_count(&self, add: PeriodCount) -> Result<Period, IntErrorKind> { |
| 46 | + match self.count.checked_add(add) { |
| 47 | + None => Err(IntErrorKind::PosOverflow), |
| 48 | + Some(count) => Ok(Self { |
| 49 | + length: self.length, |
| 50 | + count: count, |
| 51 | + }), |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + fn sub_count(&self, sub: PeriodCount) -> Result<Period, IntErrorKind> { |
| 56 | + match self.count.checked_sub(sub) { |
| 57 | + None => Err(IntErrorKind::NegOverflow), |
| 58 | + Some(count) => Ok(Self { |
| 59 | + length: self.length, |
| 60 | + count: count, |
| 61 | + }), |
| 62 | + } |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +impl PeriodsTotals for Period { |
| 67 | + fn total_time(&self) -> Result<Option<PeriodTotalTime>, TryFromIntError> { |
| 68 | + match u32::try_from(self.count) { |
| 69 | + Err(error) => Err(error), |
| 70 | + Ok(count) => Ok(self.length.checked_mul(count)), |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + fn total_time_end(&self) -> Result<Option<PeriodTotalTime>, TryFromIntError> { |
| 75 | + match u32::try_from(self.count) { |
| 76 | + Err(e) => Err(e), |
| 77 | + Ok(count) => match count.checked_add(1) { |
| 78 | + None => Ok(None), |
| 79 | + Some(count) => match self.length.checked_mul(count) { |
| 80 | + None => Ok(None), |
| 81 | + Some(time) => Ok(Some(time)), |
| 82 | + }, |
| 83 | + }, |
| 84 | + } |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +#[cfg(test)] |
| 89 | +mod test { |
| 90 | + |
| 91 | + use std::time::Duration; |
| 92 | + |
| 93 | + use crate::protocol::clock::period::{Period, PeriodsTotals}; |
| 94 | + |
| 95 | + #[test] |
| 96 | + fn it_should_get_the_total_time_of_a_period() { |
| 97 | + assert_eq!(Period::default().total_time().unwrap().unwrap(), Duration::ZERO); |
| 98 | + |
| 99 | + assert_eq!( |
| 100 | + Period::from_sec(12, 12).total_time().unwrap().unwrap(), |
| 101 | + Duration::from_secs(144) |
| 102 | + ); |
| 103 | + assert_eq!( |
| 104 | + Period::from_sec(12, 12).total_time_end().unwrap().unwrap(), |
| 105 | + Duration::from_secs(156) |
| 106 | + ); |
| 107 | + } |
| 108 | +} |
0 commit comments