-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
clock: extent and extent maker (from clock)
Create a new trait called `IncrementCounter` that uses three abstract types: `Increment` (size), `Count` (quantity), and `Product` (total). Implement this new trait in a structure called `Extent`, that uses Time Duration, Integer, and Time Duration as its types. It stores a fixed length of time with a multiplayer. Finally, define a new trait that builds extents based upon a supplied clock and the length increment. Implement this trait in two new structures, one for the working clock, and another for the stopped clock. A default is provided, selecting the working or stopped based upon the testing predicate.
- Loading branch information
Showing
3 changed files
with
205 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Self, IntErrorKind>; | ||
fn sub(&self, sub: Self::Count) -> Result<Self, IntErrorKind>; | ||
|
||
fn get(&self) -> Result<Option<Self::Product>, TryFromIntError>; | ||
fn get_next(&self) -> Result<Option<Self::Product>, 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<Self, IntErrorKind> { | ||
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<Self, IntErrorKind> { | ||
match self.quantity.checked_sub(sub) { | ||
None => Err(IntErrorKind::NegOverflow), | ||
Some(quantity) => Ok(Self { | ||
size: self.size, | ||
quantity, | ||
}), | ||
} | ||
} | ||
|
||
fn get(&self) -> Result<Option<Self::Product>, TryFromIntError> { | ||
match u32::try_from(self.quantity) { | ||
Err(error) => Err(error), | ||
Ok(quantity) => Ok(self.size.checked_mul(quantity)), | ||
} | ||
} | ||
|
||
fn get_next(&self) -> Result<Option<Self::Product>, 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) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Clock>: Sized | ||
where | ||
Clock: TimeNow, | ||
{ | ||
fn now(size: &TimeExtentIncrement) -> Option<Result<TimeExtent, TryFromIntError>> { | ||
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<Result<TimeExtent, TryFromIntError>> { | ||
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<Result<TimeExtent, TryFromIntError>> { | ||
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<const T: usize> {} | ||
|
||
pub type WorkingClockExtentMaker = ExtentClock<{ ClockType::WorkingClock as usize }>; | ||
|
||
pub type StoppedClockExtentMaker = ExtentClock<{ ClockType::StoppedClock as usize }>; | ||
|
||
impl MakeTimeExtent<WorkingClock> for WorkingClockExtentMaker {} | ||
|
||
impl MakeTimeExtent<StoppedClock> 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) | ||
); | ||
} | ||
} |