1use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
4
5use dope_core::driver::schedule::clock;
6use o3::base::time;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum Error {
10 BeforeUnixEpoch { delta: Duration },
11 NanosecondsOverflow { duration: Duration },
12 Add { source: time::Add },
13 Sub { source: time::Sub },
14 MonotonicRegression { origin: Instant, observed: Instant },
15}
16
17impl From<time::Add> for Error {
18 fn from(source: time::Add) -> Self {
19 Self::Add { source }
20 }
21}
22
23impl From<time::Sub> for Error {
24 fn from(source: time::Sub) -> Self {
25 Self::Sub { source }
26 }
27}
28
29pub trait Source: Sized {
31 fn read(&self) -> Result<time::Unix<time::Nanos>, Error>;
32
33 fn sample(&self) -> Result<(time::Unix<time::Nanos>, Instant), Error> {
37 let origin = Instant::now();
38 Ok((self.read()?, origin))
39 }
40
41 fn anchor<'d>(&self, witness: &clock::Moment<'d>) -> Result<Anchor<'d>, Error> {
45 let (epoch, observed) = self.sample()?;
46 let origin = witness.into_raw();
47 let elapsed = observed
48 .checked_duration_since(origin)
49 .ok_or(Error::MonotonicRegression { origin, observed })?;
50 Ok(Anchor {
51 epoch: epoch.try_sub(elapsed)?,
52 origin: *witness,
53 })
54 }
55}
56
57#[derive(Clone, Copy, Default)]
58pub enum System {
59 #[default]
60 Wall,
61}
62
63impl System {
64 pub const NEW: Self = Self::Wall;
65}
66
67impl Source for System {
68 fn read(&self) -> Result<time::Unix<time::Nanos>, Error> {
69 let now = SystemTime::now();
70 let duration = match now.duration_since(UNIX_EPOCH) {
71 Ok(duration) => duration,
72 Err(cause) => {
73 return Err(Error::BeforeUnixEpoch {
74 delta: cause.duration(),
75 });
76 }
77 };
78 let nanos = u64::try_from(duration.as_nanos())
79 .map_err(|_| Error::NanosecondsOverflow { duration })?;
80 Ok(time::Unix::new(nanos))
81 }
82}
83
84#[derive(Clone, Copy)]
86pub struct Anchor<'d> {
87 epoch: time::Unix<time::Nanos>,
88 origin: clock::Moment<'d>,
89}
90
91impl<'d> Anchor<'d> {
92 pub fn observe(&self, witness: &clock::Moment<'d>) -> Result<time::Unix<time::Nanos>, Error> {
93 let elapsed = witness
94 .try_since(self.origin)
95 .ok_or_else(|| Error::MonotonicRegression {
96 origin: self.origin.into_raw(),
97 observed: witness.into_raw(),
98 })?;
99 Ok(self.epoch.try_add(elapsed)?)
100 }
101}
102
103const _: () = {
104 assert!(std::mem::size_of::<System>() == 0);
105 assert!(
106 std::mem::size_of::<Anchor<'static>>()
107 == std::mem::size_of::<time::Unix<time::Nanos>>()
108 + std::mem::size_of::<clock::Moment<'static>>()
109 );
110 assert!(!std::mem::needs_drop::<Anchor<'static>>());
111};