diff --git a/src/protocol/clock.rs b/src/protocol/clock/mod.rs similarity index 98% rename from src/protocol/clock.rs rename to src/protocol/clock/mod.rs index a72f3699e..6e925df3c 100644 --- a/src/protocol/clock.rs +++ b/src/protocol/clock/mod.rs @@ -1,5 +1,5 @@ use std::num::IntErrorKind; -pub use std::time::Duration; +use std::time::Duration; pub type DurationSinceUnixEpoch = Duration; @@ -240,9 +240,12 @@ mod stopped_clock { #[test] fn it_should_get_app_start_time() { - const TIME_AT_WRITING_THIS_TEST: Duration = Duration::new(1662983731, 000022312); + const TIME_AT_WRITING_THIS_TEST: Duration = Duration::new(1662983731, 22312); assert!(get_app_start_time() > TIME_AT_WRITING_THIS_TEST); } } } } + +pub mod timeextent; +pub mod timeextentmaker; diff --git a/src/protocol/clock/timeextent.rs b/src/protocol/clock/timeextent.rs new file mode 100644 index 000000000..370b32ff4 --- /dev/null +++ b/src/protocol/clock/timeextent.rs @@ -0,0 +1,112 @@ +use std::num::{IntErrorKind, TryFromIntError}; +use std::time::Duration; + +/// Count fixed sized increments. Provides the product of the increment and its count. +pub trait Extent: Sized + Default { + type Increment; + type Count; + type Product; + + fn new(size: &Self::Increment, quantity: &Self::Count) -> Self; + + fn add(&self, add: Self::Count) -> Result; + fn sub(&self, sub: Self::Count) -> Result; + + fn get(&self) -> Result, TryFromIntError>; + fn get_next(&self) -> Result, TryFromIntError>; +} + +pub type TimeExtentIncrement = Duration; +pub type TimeExtentIncrementSeconds = u64; +pub type TimeExtentCount = u64; +pub type TimeExtentExtent = TimeExtentIncrement; + +#[derive(Debug, Default, Hash, PartialEq, Eq)] +pub struct TimeExtent { + pub size: TimeExtentIncrement, + pub quantity: TimeExtentCount, +} + +impl TimeExtent { + pub const fn from_sec(size: TimeExtentIncrementSeconds, quantity: &TimeExtentCount) -> Self { + Self { + size: TimeExtentIncrement::from_secs(size), + quantity: *quantity, + } + } +} + +impl Extent for TimeExtent { + type Increment = TimeExtentIncrement; + type Count = TimeExtentCount; + type Product = TimeExtentExtent; + + fn new(size: &Self::Increment, quantity: &Self::Count) -> Self { + Self { + size: *size, + quantity: *quantity, + } + } + + fn add(&self, add: Self::Count) -> Result { + match self.quantity.checked_add(add) { + None => Err(IntErrorKind::PosOverflow), + Some(quantity) => Ok(Self { + size: self.size, + quantity, + }), + } + } + + fn sub(&self, sub: Self::Count) -> Result { + match self.quantity.checked_sub(sub) { + None => Err(IntErrorKind::NegOverflow), + Some(quantity) => Ok(Self { + size: self.size, + quantity, + }), + } + } + + fn get(&self) -> Result, TryFromIntError> { + match u32::try_from(self.quantity) { + Err(error) => Err(error), + Ok(quantity) => Ok(self.size.checked_mul(quantity)), + } + } + + fn get_next(&self) -> Result, TryFromIntError> { + match u32::try_from(self.quantity) { + Err(e) => Err(e), + Ok(quantity) => match quantity.checked_add(1) { + None => Ok(None), + Some(quantity) => match self.size.checked_mul(quantity) { + None => Ok(None), + Some(extent) => Ok(Some(extent)), + }, + }, + } + } +} + +#[cfg(test)] +mod test { + + use std::time::Duration; + + use crate::protocol::clock::timeextent::{Extent, TimeExtent}; + + #[test] + fn it_should_get_the_total_time_of_a_period() { + assert_eq!(TimeExtent::default().get().unwrap().unwrap(), Duration::ZERO); + + assert_eq!( + TimeExtent::from_sec(12, &12).get().unwrap().unwrap(), + Duration::from_secs(144) + ); + assert_eq!( + TimeExtent::from_sec(12, &12).get_next().unwrap().unwrap(), + Duration::from_secs(156) + ); + } +} diff --git a/src/protocol/clock/timeextentmaker.rs b/src/protocol/clock/timeextentmaker.rs new file mode 100644 index 000000000..6979d2825 --- /dev/null +++ b/src/protocol/clock/timeextentmaker.rs @@ -0,0 +1,88 @@ +use std::num::TryFromIntError; +use std::time::Duration; + +use super::timeextent::{Extent, TimeExtent, TimeExtentCount, TimeExtentIncrement}; +use super::{ClockType, StoppedClock, TimeNow, WorkingClock}; + +pub trait MakeTimeExtent: Sized +where + Clock: TimeNow, +{ + fn now(size: &TimeExtentIncrement) -> Option> { + Clock::now() + .as_nanos() + .checked_div((*size).as_nanos()) + .map(|quantity| match TimeExtentCount::try_from(quantity) { + Err(error) => Err(error), + Ok(quantity) => Ok(TimeExtent::new(size, &quantity)), + }) + } + + fn now_add(size: &TimeExtentIncrement, add_time: &Duration) -> Option> { + match Clock::add(add_time) { + None => None, + Some(time) => { + time.as_nanos() + .checked_div(size.as_nanos()) + .map(|quantity| match TimeExtentCount::try_from(quantity) { + Err(error) => Err(error), + Ok(quantity) => Ok(TimeExtent::new(size, &quantity)), + }) + } + } + } + fn now_sub(size: &TimeExtentIncrement, sub_time: &Duration) -> Option> { + match Clock::sub(sub_time) { + None => None, + Some(time) => { + time.as_nanos() + .checked_div(size.as_nanos()) + .map(|quantity| match TimeExtentCount::try_from(quantity) { + Err(error) => Err(error), + Ok(quantity) => Ok(TimeExtent::new(size, &quantity)), + }) + } + } + } +} + +#[derive(Debug)] +pub struct ExtentClock {} + +pub type WorkingClockExtentMaker = ExtentClock<{ ClockType::WorkingClock as usize }>; + +pub type StoppedClockExtentMaker = ExtentClock<{ ClockType::StoppedClock as usize }>; + +impl MakeTimeExtent for WorkingClockExtentMaker {} + +impl MakeTimeExtent for StoppedClockExtentMaker {} + +#[cfg(not(test))] +pub type DefaultClockExtentMaker = WorkingClockExtentMaker; + +#[cfg(test)] +pub type DefaultClockExtentMaker = StoppedClockExtentMaker; + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use crate::protocol::clock::timeextent::TimeExtent; + use crate::protocol::clock::timeextentmaker::{DefaultClockExtentMaker, MakeTimeExtent}; + use crate::protocol::clock::{DefaultClock, DurationSinceUnixEpoch, StoppedTime}; + + #[test] + fn it_should_get_the_current_period() { + assert_eq!( + DefaultClockExtentMaker::now(&Duration::from_secs(2)).unwrap().unwrap(), + TimeExtent::from_sec(2, &0) + ); + + DefaultClock::local_set(&DurationSinceUnixEpoch::from_secs(12387687123)); + + assert_eq!( + DefaultClockExtentMaker::now(&Duration::from_secs(2)).unwrap().unwrap(), + TimeExtent::from_sec(2, &6193843561) + ); + } +}