chiark / gitweb /
changelog: document further make-release changes
[otter.git] / support / fake-time.rs
1 // Copyright 2020-2021 Ian Jackson and contributors to Otter
2 // SPDX-License-Identifier: AGPL-3.0-or-later
3 // There is NO WARRANTY.
4
5 use crate::prelude::*;
6
7 use parking_lot::Mutex;
8
9 type Millis = u32;
10 type Micros = u64;
11
12 #[derive(Serialize,Deserialize,Error,Debug,Clone)]
13 #[error("Time is real")]
14 pub struct TimeIsReal;
15
16 #[derive(Deserialize,Debug,Clone,Default)]
17 #[serde(transparent)]
18 pub struct FakeTimeConfig(pub Option<FakeTimeSpec>);
19
20 #[derive(Deserialize,Serialize,Debug,Clone,Default)]
21 #[serde(into="Vec<Millis>", try_from="Vec<Millis>")]
22 pub struct FakeTimeSpec(pub Option<Millis>);
23
24 #[derive(Error,Debug)]
25 #[error("invalid fake time: must be list of 0 or 1 numbers (ms)")]
26 pub struct InvalidFakeTime;
27
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 {
33       [] => None,
34       &[ms] => Some(ms),
35       _ => throw!(InvalidFakeTime),
36     })
37   }
38 }
39
40 impl Into<Vec<Millis>> for FakeTimeSpec {
41   fn into(self) -> Vec<Millis> {
42     self.0.into_iter().collect()
43   }
44 }
45
46 #[derive(Debug)]
47 pub struct GlobalClock {
48   fakeable: Option<Mutex<Option<FakeClock>>>,
49 }
50
51 #[derive(Debug)]
52 struct FakeClock {
53   start: Instant,
54   current: Micros,
55 }
56
57 impl FakeClock {
58   fn from_millis(ms: Millis) -> FakeClock { FakeClock {
59     start: Instant::now(),
60     current: (ms as Micros) * 1000,
61   } }
62
63   fn from_spec(FakeTimeSpec(fspec): FakeTimeSpec) -> Option<FakeClock> {
64     fspec.map(FakeClock::from_millis)
65   }
66 }
67
68 impl FakeTimeConfig {
69   pub fn make_global_clock(self) -> GlobalClock {
70     let fakeable = self.0.map(|fspec| FakeClock::from_spec(fspec).into());
71     GlobalClock { fakeable }
72   }
73 }
74
75 impl GlobalClock {
76   pub fn now(&self) -> Instant {
77     self.now_fake().unwrap_or_else(|| Instant::now())
78   }
79
80   #[throws(as Option)]
81   fn now_fake(&self) -> Instant {
82     let mut guard = self.fakeable.as_ref()?.lock();
83     let fake = guard.as_mut()?;
84     fake.current += 1;
85     fake.start + Duration::from_micros(fake.current)
86   }
87
88   #[throws(TimeIsReal)]
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)
92   }
93
94   pub fn is_fake(&self) -> bool {
95     self.fakeable.is_some()
96   }
97