1 // Copyright 2020-2021 Ian Jackson and contributors to Otter
2 // SPDX-License-Identifier: AGPL-3.0-or-later
3 // There is NO WARRANTY.
7 use parking_lot::Mutex;
12 #[derive(Serialize,Deserialize,Error,Debug,Clone)]
13 #[error("Time is real")]
14 pub struct TimeIsReal;
16 #[derive(Deserialize,Debug,Clone,Default)]
18 pub struct FakeTimeConfig(pub Option<FakeTimeSpec>);
20 #[derive(Deserialize,Serialize,Debug,Clone,Default)]
21 #[serde(into="Vec<Millis>", try_from="Vec<Millis>")]
22 pub struct FakeTimeSpec(pub Option<Millis>);
24 #[derive(Error,Debug)]
25 #[error("invalid fake time: must be list of 0 or 1 numbers (ms)")]
26 pub struct InvalidFakeTime;
28 impl TryFrom<Vec<Millis>> for FakeTimeSpec {
29 type Error = InvalidFakeTime;
30 #[throws(InvalidFakeTime)]
31 fn try_from(l: Vec<Millis>) -> FakeTimeSpec {
32 FakeTimeSpec(match &*l {
35 _ => throw!(InvalidFakeTime),
40 impl Into<Vec<Millis>> for FakeTimeSpec {
41 fn into(self) -> Vec<Millis> {
42 self.0.into_iter().collect()
47 pub struct GlobalClock {
48 fakeable: Option<Mutex<Option<FakeClock>>>,
58 fn from_millis(ms: Millis) -> FakeClock { FakeClock {
59 start: Instant::now(),
60 current: (ms as Micros) * 1000,
63 fn from_spec(FakeTimeSpec(fspec): FakeTimeSpec) -> Option<FakeClock> {
64 fspec.map(FakeClock::from_millis)
69 pub fn make_global_clock(self) -> GlobalClock {
70 let fakeable = self.0.map(|fspec| FakeClock::from_spec(fspec).into());
71 GlobalClock { fakeable }
76 pub fn now(&self) -> Instant {
77 self.now_fake().unwrap_or_else(|| Instant::now())
81 fn now_fake(&self) -> Instant {
82 let mut guard = self.fakeable.as_ref()?.lock();
83 let fake = guard.as_mut()?;
85 fake.start + Duration::from_micros(fake.current)
89 pub fn set_fake(&self, fspec: FakeTimeSpec, _: AuthorisationSuperuser) {
90 let mut guard = self.fakeable.as_ref().ok_or(TimeIsReal)?.lock();
91 *guard = FakeClock::from_spec(fspec)
94 pub fn is_fake(&self) -> bool {
95 self.fakeable.is_some()