Skip to content

Commit

Permalink
clock: time extent, maker, and associated traits
Browse files Browse the repository at this point in the history
`TimeExtent` is a simple structure that contains base increment (duration), and amount (a multiplier for the base the base increment). The resulting product of the base and the multiplier is the total duration of the time extent.

`TimeExtentClock` is a helper that generates time extents based upon the increment length and the time of the clock, this clock is specialised according to the `test` predicate with the `DefaultClockExtentMaker` type.
  • Loading branch information
da2ce7 committed Sep 20, 2022
1 parent 028e40b commit e95b760
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 2 deletions.
6 changes: 4 additions & 2 deletions src/protocol/clock.rs → src/protocol/clock/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::num::IntErrorKind;
pub use std::time::Duration;
use std::time::Duration;

pub type DurationSinceUnixEpoch = Duration;

Expand Down Expand Up @@ -240,9 +240,11 @@ 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;
185 changes: 185 additions & 0 deletions src/protocol/clock/timeextent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
use std::num::{IntErrorKind, TryFromIntError};
use std::time::Duration;

use super::{ClockType, StoppedClock, TimeNow, WorkingClock};

pub trait Extent: Sized + Default {
type Base;
type Multiplier;
type Product;

fn new(unit: &Self::Base, count: &Self::Multiplier) -> Self;

fn add(&self, add: Self::Multiplier) -> Result<Self, IntErrorKind>;
fn sub(&self, sub: Self::Multiplier) -> Result<Self, IntErrorKind>;

fn total(&self) -> Result<Option<Self::Product>, TryFromIntError>;
fn total_next(&self) -> Result<Option<Self::Product>, TryFromIntError>;
}

pub type TimeExtentBase = Duration;
pub type TimeExtentMultiplier = u64;
pub type TimeExtentProduct = TimeExtentBase;

#[derive(Debug, Default, Hash, PartialEq, Eq)]
pub struct TimeExtent {
pub increment: TimeExtentBase,
pub amount: TimeExtentMultiplier,
}

impl TimeExtent {
pub const fn from_sec(seconds: u64, amount: &TimeExtentMultiplier) -> Self {
Self {
increment: TimeExtentBase::from_secs(seconds),
amount: *amount,
}
}
}

impl Extent for TimeExtent {
type Base = TimeExtentBase;
type Multiplier = TimeExtentMultiplier;
type Product = TimeExtentProduct;

fn new(increment: &Self::Base, amount: &Self::Multiplier) -> Self {
Self {
increment: *increment,
amount: *amount,
}
}

fn add(&self, add: Self::Multiplier) -> Result<Self, IntErrorKind> {
match self.amount.checked_add(add) {
None => Err(IntErrorKind::PosOverflow),
Some(amount) => Ok(Self {
increment: self.increment,
amount,
}),
}
}

fn sub(&self, sub: Self::Multiplier) -> Result<Self, IntErrorKind> {
match self.amount.checked_sub(sub) {
None => Err(IntErrorKind::NegOverflow),
Some(amount) => Ok(Self {
increment: self.increment,
amount,
}),
}
}

fn total(&self) -> Result<Option<Self::Product>, TryFromIntError> {
match u32::try_from(self.amount) {
Err(error) => Err(error),
Ok(amount) => Ok(self.increment.checked_mul(amount)),
}
}

fn total_next(&self) -> Result<Option<Self::Product>, TryFromIntError> {
match u32::try_from(self.amount) {
Err(e) => Err(e),
Ok(amount) => match amount.checked_add(1) {
None => Ok(None),
Some(amount) => match self.increment.checked_mul(amount) {
None => Ok(None),
Some(extent) => Ok(Some(extent)),
},
},
}
}
}

pub trait MakeTimeExtent<Clock>: Sized
where
Clock: TimeNow,
{
fn now(increment: &TimeExtentBase) -> Option<Result<TimeExtent, TryFromIntError>> {
Clock::now()
.as_nanos()
.checked_div((*increment).as_nanos())
.map(|amount| match TimeExtentMultiplier::try_from(amount) {
Err(error) => Err(error),
Ok(amount) => Ok(TimeExtent::new(increment, &amount)),
})
}

fn now_add(increment: &TimeExtentBase, add_time: &Duration) -> Option<Result<TimeExtent, TryFromIntError>> {
match Clock::add(add_time) {
None => None,
Some(time) => {
time.as_nanos()
.checked_div(increment.as_nanos())
.map(|amount| match TimeExtentMultiplier::try_from(amount) {
Err(error) => Err(error),
Ok(amount) => Ok(TimeExtent::new(increment, &amount)),
})
}
}
}
fn now_sub(increment: &TimeExtentBase, sub_time: &Duration) -> Option<Result<TimeExtent, TryFromIntError>> {
match Clock::sub(sub_time) {
None => None,
Some(time) => {
time.as_nanos()
.checked_div(increment.as_nanos())
.map(|amount| match TimeExtentMultiplier::try_from(amount) {
Err(error) => Err(error),
Ok(amount) => Ok(TimeExtent::new(increment, &amount)),
})
}
}
}
}

#[derive(Debug)]
pub struct TimeExtentClock<const T: usize> {}

pub type WorkingClockExtentMaker = TimeExtentClock<{ ClockType::WorkingClock as usize }>;
pub type StoppedClockExtentMaker = TimeExtentClock<{ 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 test {

use std::time::Duration;

use crate::protocol::clock::timeextent::{DefaultClockExtentMaker, Extent, MakeTimeExtent, TimeExtent};
use crate::protocol::clock::{DefaultClock, DurationSinceUnixEpoch, StoppedTime};

#[test]
fn it_should_get_the_total_duration() {
assert_eq!(TimeExtent::default().total().unwrap().unwrap(), Duration::ZERO);

assert_eq!(
TimeExtent::from_sec(12, &12).total().unwrap().unwrap(),
Duration::from_secs(144)
);
assert_eq!(
TimeExtent::from_sec(12, &12).total_next().unwrap().unwrap(),
Duration::from_secs(156)
);
}

#[test]
fn it_should_make_the_current_extent() {
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)
);
}
}

0 comments on commit e95b760

Please sign in to comment.