Skip to content

Commit 028644d

Browse files
committed
clock: time extent, maker, and associated traits
`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. `TimeExtentMaker` 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 `DefaultClockTimeExtentMaker` type.
1 parent 8da9963 commit 028644d

File tree

2 files changed

+189
-2
lines changed

2 files changed

+189
-2
lines changed

src/protocol/clock.rs renamed to src/protocol/clock/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::num::IntErrorKind;
2-
pub use std::time::Duration;
2+
use std::time::Duration;
33

44
pub type DurationSinceUnixEpoch = Duration;
55

@@ -240,9 +240,11 @@ mod stopped_clock {
240240

241241
#[test]
242242
fn it_should_get_app_start_time() {
243-
const TIME_AT_WRITING_THIS_TEST: Duration = Duration::new(1662983731, 000022312);
243+
const TIME_AT_WRITING_THIS_TEST: Duration = Duration::new(1662983731, 22312);
244244
assert!(get_app_start_time() > TIME_AT_WRITING_THIS_TEST);
245245
}
246246
}
247247
}
248248
}
249+
250+
pub mod timeextent;

src/protocol/clock/timeextent.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use std::num::{IntErrorKind, TryFromIntError};
2+
use std::time::Duration;
3+
4+
use super::{ClockType, StoppedClock, TimeNow, WorkingClock};
5+
6+
pub trait Extent: Sized + Default {
7+
type Base;
8+
type Multiplier;
9+
type Product;
10+
11+
fn new(unit: &Self::Base, count: &Self::Multiplier) -> Self;
12+
13+
fn add(&self, add: Self::Multiplier) -> Result<Self, IntErrorKind>;
14+
fn sub(&self, sub: Self::Multiplier) -> Result<Self, IntErrorKind>;
15+
16+
fn total(&self) -> Result<Option<Self::Product>, TryFromIntError>;
17+
fn total_next(&self) -> Result<Option<Self::Product>, TryFromIntError>;
18+
}
19+
20+
pub type TimeExtentBase = Duration;
21+
pub type TimeExtentMultiplier = u64;
22+
pub type TimeExtentProduct = TimeExtentBase;
23+
24+
#[derive(Debug, Default, Hash, PartialEq, Eq)]
25+
pub struct TimeExtent {
26+
pub increment: TimeExtentBase,
27+
pub amount: TimeExtentMultiplier,
28+
}
29+
30+
impl TimeExtent {
31+
pub const fn from_sec(seconds: u64, amount: &TimeExtentMultiplier) -> Self {
32+
Self {
33+
increment: TimeExtentBase::from_secs(seconds),
34+
amount: *amount,
35+
}
36+
}
37+
}
38+
39+
impl Extent for TimeExtent {
40+
type Base = TimeExtentBase;
41+
type Multiplier = TimeExtentMultiplier;
42+
type Product = TimeExtentProduct;
43+
44+
fn new(increment: &Self::Base, amount: &Self::Multiplier) -> Self {
45+
Self {
46+
increment: *increment,
47+
amount: *amount,
48+
}
49+
}
50+
51+
fn add(&self, add: Self::Multiplier) -> Result<Self, IntErrorKind> {
52+
match self.amount.checked_add(add) {
53+
None => Err(IntErrorKind::PosOverflow),
54+
Some(amount) => Ok(Self {
55+
increment: self.increment,
56+
amount,
57+
}),
58+
}
59+
}
60+
61+
fn sub(&self, sub: Self::Multiplier) -> Result<Self, IntErrorKind> {
62+
match self.amount.checked_sub(sub) {
63+
None => Err(IntErrorKind::NegOverflow),
64+
Some(amount) => Ok(Self {
65+
increment: self.increment,
66+
amount,
67+
}),
68+
}
69+
}
70+
71+
fn total(&self) -> Result<Option<Self::Product>, TryFromIntError> {
72+
match u32::try_from(self.amount) {
73+
Err(error) => Err(error),
74+
Ok(amount) => Ok(self.increment.checked_mul(amount)),
75+
}
76+
}
77+
78+
fn total_next(&self) -> Result<Option<Self::Product>, TryFromIntError> {
79+
match u32::try_from(self.amount) {
80+
Err(e) => Err(e),
81+
Ok(amount) => match amount.checked_add(1) {
82+
None => Ok(None),
83+
Some(amount) => match self.increment.checked_mul(amount) {
84+
None => Ok(None),
85+
Some(extent) => Ok(Some(extent)),
86+
},
87+
},
88+
}
89+
}
90+
}
91+
92+
pub trait MakeTimeExtent<Clock>: Sized
93+
where
94+
Clock: TimeNow,
95+
{
96+
fn now(increment: &TimeExtentBase) -> Option<Result<TimeExtent, TryFromIntError>> {
97+
Clock::now()
98+
.as_nanos()
99+
.checked_div((*increment).as_nanos())
100+
.map(|amount| match TimeExtentMultiplier::try_from(amount) {
101+
Err(error) => Err(error),
102+
Ok(amount) => Ok(TimeExtent::new(increment, &amount)),
103+
})
104+
}
105+
106+
fn now_add(increment: &TimeExtentBase, add_time: &Duration) -> Option<Result<TimeExtent, TryFromIntError>> {
107+
match Clock::add(add_time) {
108+
None => None,
109+
Some(time) => {
110+
time.as_nanos()
111+
.checked_div(increment.as_nanos())
112+
.map(|amount| match TimeExtentMultiplier::try_from(amount) {
113+
Err(error) => Err(error),
114+
Ok(amount) => Ok(TimeExtent::new(increment, &amount)),
115+
})
116+
}
117+
}
118+
}
119+
fn now_sub(increment: &TimeExtentBase, sub_time: &Duration) -> Option<Result<TimeExtent, TryFromIntError>> {
120+
match Clock::sub(sub_time) {
121+
None => None,
122+
Some(time) => {
123+
time.as_nanos()
124+
.checked_div(increment.as_nanos())
125+
.map(|amount| match TimeExtentMultiplier::try_from(amount) {
126+
Err(error) => Err(error),
127+
Ok(amount) => Ok(TimeExtent::new(increment, &amount)),
128+
})
129+
}
130+
}
131+
}
132+
}
133+
134+
#[derive(Debug)]
135+
pub struct TimeExtentMaker<const CLOCK_TYPE: usize> {}
136+
137+
pub type WorkingClockTimeExtentMaker = TimeExtentMaker<{ ClockType::WorkingClock as usize }>;
138+
pub type StoppedClockTimeExtentMaker = TimeExtentMaker<{ ClockType::StoppedClock as usize }>;
139+
140+
impl MakeTimeExtent<WorkingClock> for WorkingClockTimeExtentMaker {}
141+
impl MakeTimeExtent<StoppedClock> for StoppedClockTimeExtentMaker {}
142+
143+
#[cfg(not(test))]
144+
pub type DefaultClockTimeExtentMaker = WorkingClockTimeExtentMaker;
145+
146+
#[cfg(test)]
147+
pub type DefaultClockTimeExtentMaker = StoppedClockTimeExtentMaker;
148+
149+
#[cfg(test)]
150+
mod test {
151+
152+
use std::time::Duration;
153+
154+
use crate::protocol::clock::timeextent::{DefaultClockTimeExtentMaker, Extent, MakeTimeExtent, TimeExtent};
155+
use crate::protocol::clock::{DefaultClock, DurationSinceUnixEpoch, StoppedTime};
156+
157+
#[test]
158+
fn it_should_get_the_total_duration() {
159+
assert_eq!(TimeExtent::default().total().unwrap().unwrap(), Duration::ZERO);
160+
161+
assert_eq!(
162+
TimeExtent::from_sec(12, &12).total().unwrap().unwrap(),
163+
Duration::from_secs(144)
164+
);
165+
assert_eq!(
166+
TimeExtent::from_sec(12, &12).total_next().unwrap().unwrap(),
167+
Duration::from_secs(156)
168+
);
169+
}
170+
171+
#[test]
172+
fn it_should_make_the_current_extent() {
173+
assert_eq!(
174+
DefaultClockTimeExtentMaker::now(&Duration::from_secs(2)).unwrap().unwrap(),
175+
TimeExtent::from_sec(2, &0)
176+
);
177+
178+
DefaultClock::local_set(&DurationSinceUnixEpoch::from_secs(12387687123));
179+
180+
assert_eq!(
181+
DefaultClockTimeExtentMaker::now(&Duration::from_secs(2)).unwrap().unwrap(),
182+
TimeExtent::from_sec(2, &6193843561)
183+
);
184+
}
185+
}

0 commit comments

Comments
 (0)