//! Time stamps and related functionality.

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

use serde::{Deserialize, Serialize};

/// A point in time.
///
/// This can be stored persistently.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Timestamp {
    unix: u64,
}

impl Timestamp {
    /// Current time.
    pub fn now() -> Result<Self, TimeError> {
        let duration = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map_err(TimeError::Unix)?;
        Ok(Self {
            unix: duration.as_secs(),
        })
    }
}

impl From<&Timestamp> for u64 {
    fn from(value: &Timestamp) -> Self {
        value.unix
    }
}

/// Time is out of joint.
#[derive(Debug, thiserror::Error)]
pub enum TimeError {
    /// Can't compute Unix time.
    #[error("failed to calculate current Unix time stamp")]
    Unix(#[source] SystemTimeError),
}

#[cfg(test)]
mod test {
    use std::{thread::sleep, time::Duration};

    use super::*;

    type AnyResult<T> = Result<T, Box<dyn std::error::Error>>;

    #[test]
    fn current_time() -> AnyResult<()> {
        let t1 = Timestamp::now()?;
        sleep(Duration::from_secs(2));
        let t2 = Timestamp::now()?;

        let t1: u64 = (&t1).into();
        let t2: u64 = (&t2).into();

        assert!(t2 - t1 > 0);
        Ok(())
    }
}
