diff --git a/src/protocol/clock/extent.rs b/src/protocol/clock/extent.rs new file mode 100644 index 000000000..6c9e51157 --- /dev/null +++ b/src/protocol/clock/extent.rs @@ -0,0 +1,112 @@ +use std::num::{IntErrorKind, TryFromIntError}; +use std::time::Duration; + +pub trait IncrementCounter: Sized + Default { + type Increment; + + type IncrementCount; + type IncrementProduct; + + fn new(size: &Self::Increment, quantity: &Self::IncrementCount) -> Self; + + fn add(&self, add: Self::IncrementCount) -> Result; + fn sub(&self, sub: Self::IncrementCount) -> Result; + + fn product_current(&self) -> Result, TryFromIntError>; + fn product_next(&self) -> Result, TryFromIntError>; +} + +pub type ExtentUnit = Duration; +pub type ExtentUnitSeconds = u64; +pub type ExtentCount = u64; +pub type ExtentProduct = ExtentUnit; + +#[derive(Debug, Default, Hash, PartialEq, Eq)] +pub struct Extent { + pub size: ExtentUnit, + pub quantity: ExtentCount, +} + +impl Extent { + pub const fn from_sec(size: ExtentUnitSeconds, quantity: &ExtentCount) -> Self { + Self { + size: ExtentUnit::from_secs(size), + quantity: *quantity, + } + } +} + +impl IncrementCounter for Extent { + type Increment = ExtentUnit; + type IncrementCount = ExtentCount; + type IncrementProduct = ExtentProduct; + + fn new(size: &Self::Increment, quantity: &Self::IncrementCount) -> Self { + Self { + size: *size, + quantity: *quantity, + } + } + + fn add(&self, add: Self::IncrementCount) -> Result { + match self.quantity.checked_add(add) { + None => Err(IntErrorKind::PosOverflow), + Some(quantity) => Ok(Self { + size: self.size, + quantity, + }), + } + } + + fn sub(&self, sub: Self::IncrementCount) -> Result { + match self.quantity.checked_sub(sub) { + None => Err(IntErrorKind::NegOverflow), + Some(quantity) => Ok(Self { + size: self.size, + quantity, + }), + } + } + + fn product_current(&self) -> Result, TryFromIntError> { + match u32::try_from(self.quantity) { + Err(error) => Err(error), + Ok(quantity) => Ok(self.size.checked_mul(quantity)), + } + } + + fn product_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::extent::{Extent, IncrementCounter}; + + #[test] + fn it_should_get_the_total_time_of_a_period() { + assert_eq!(Extent::default().product_current().unwrap().unwrap(), Duration::ZERO); + + assert_eq!( + Extent::from_sec(12, &12).product_current().unwrap().unwrap(), + Duration::from_secs(144) + ); + assert_eq!( + Extent::from_sec(12, &12).product_next().unwrap().unwrap(), + Duration::from_secs(156) + ); + } +} diff --git a/src/protocol/clock/extentmaker.rs b/src/protocol/clock/extentmaker.rs new file mode 100644 index 000000000..7fc5d6515 --- /dev/null +++ b/src/protocol/clock/extentmaker.rs @@ -0,0 +1,86 @@ +use std::num::TryFromIntError; +use std::time::Duration; + +use super::extent::{Extent, ExtentCount, ExtentUnit, IncrementCounter}; +use super::{ClockType, StoppedClock, TimeNow, WorkingClock}; + +pub trait ExtentFromClock: Sized +where + T: TimeNow, +{ + fn now(size: &ExtentUnit) -> Option> { + T::now() + .as_nanos() + .checked_div((*size).as_nanos()) + .map(|quantity| match ExtentCount::try_from(quantity) { + Err(error) => Err(error), + Ok(quantity) => Ok(Extent::new(size, &quantity)), + }) + } + + fn now_add(size: &ExtentUnit, add_time: &Duration) -> Option> { + match T::add(add_time) { + None => None, + Some(time) => time + .as_nanos() + .checked_div(size.as_nanos()) + .map(|quantity| match ExtentCount::try_from(quantity) { + Err(error) => Err(error), + Ok(quantity) => Ok(Extent::new(size, &quantity)), + }), + } + } + fn now_sub(size: &ExtentUnit, sub_time: &Duration) -> Option> { + match T::sub(sub_time) { + None => None, + Some(time) => time + .as_nanos() + .checked_div(size.as_nanos()) + .map(|quantity| match ExtentCount::try_from(quantity) { + Err(error) => Err(error), + Ok(quantity) => Ok(Extent::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 ExtentFromClock for WorkingClockExtentMaker {} + +impl ExtentFromClock 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::extent::Extent; + use crate::protocol::clock::extentmaker::{DefaultClockExtentMaker, ExtentFromClock}; + 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(), + Extent::from_sec(2, &0) + ); + + DefaultClock::local_set(&DurationSinceUnixEpoch::from_secs(12387687123)); + + assert_eq!( + DefaultClockExtentMaker::now(&Duration::from_secs(2)).unwrap().unwrap(), + Extent::from_sec(2, &6193843561) + ); + } +} 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..856326a5f 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 extent; +pub mod extentmaker;