liminal-server 0.20.0

Standalone server for the liminal messaging bus
Documentation
//! The one wall-clock reading the console's records are timed from.
//!
//! The wall clock is read once, when the hub is built, and every message's
//! time is that reading plus the monotonic time elapsed since. A per-message
//! clock read could fail (a clock set before the epoch) or step backwards;
//! this cannot, and its one failure happens at start, where it is named.

use std::time::{Instant, SystemTime, UNIX_EPOCH};

use super::error::ConsoleError;

/// A wall-clock reading in milliseconds since the epoch, paired with the
/// monotonic instant it was taken at.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct WallBase {
    unix_ms: u64,
    at: Instant,
}

impl WallBase {
    /// Reads the wall clock now.
    ///
    /// # Errors
    /// Returns [`ConsoleError::Clock`] when the wall clock reads before the
    /// epoch or past what milliseconds in a `u64` can hold.
    pub fn now() -> Result<Self, ConsoleError> {
        let at = Instant::now();
        let since_epoch = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map_err(|error| ConsoleError::Clock {
                reason: format!("the wall clock reads before the epoch: {error}"),
            })?;
        let unix_ms = u64::try_from(since_epoch.as_millis()).map_err(|_| ConsoleError::Clock {
            reason: "the wall clock is past u64 milliseconds".to_owned(),
        })?;
        Ok(Self { unix_ms, at })
    }

    /// A base fixed at `unix_ms` for `at`, for records whose times must be
    /// reproducible.
    #[must_use]
    pub const fn fixed(unix_ms: u64, at: Instant) -> Self {
        Self { unix_ms, at }
    }

    /// Wall time, in milliseconds since the epoch, of the monotonic instant
    /// `seen_at`. An instant before the base reads as the base.
    ///
    /// # Errors
    /// Returns [`ConsoleError::Clock`] when the sum passes u64 milliseconds.
    pub fn unix_ms_at(&self, seen_at: Instant) -> Result<u64, ConsoleError> {
        self.unix_ms_of(seen_at.max(self.at))
    }

    /// Wall time, in milliseconds since the epoch, of the monotonic instant
    /// `at`, before or after the base.
    ///
    /// # Errors
    /// Returns [`ConsoleError::Clock`] when it falls outside u64
    /// milliseconds.
    pub fn unix_ms_of(&self, at: Instant) -> Result<u64, ConsoleError> {
        let wall = if at >= self.at {
            u64::try_from(at.duration_since(self.at).as_millis())
                .ok()
                .and_then(|after| self.unix_ms.checked_add(after))
        } else {
            u64::try_from(self.at.duration_since(at).as_millis())
                .ok()
                .and_then(|before| self.unix_ms.checked_sub(before))
        };
        wall.ok_or_else(|| ConsoleError::Clock {
            reason: "a time is outside u64 milliseconds since the epoch".to_owned(),
        })
    }
}