1use std::ffi::OsString;
5use std::time::{SystemTime, UNIX_EPOCH};
6
7use crate::StoreError;
8
9pub const EPOCH_ENV: &str = "SOURCE_DATE_EPOCH";
11
12pub trait Clock {
14 fn now(&self) -> Result<u64, StoreError>;
20}
21
22#[derive(Debug, Clone, Default)]
24pub struct SystemClock {
25 epoch: Option<OsString>,
26}
27
28impl SystemClock {
29 #[must_use]
31 pub fn from_env() -> Self {
32 Self::with_epoch(std::env::var_os(EPOCH_ENV))
33 }
34
35 #[must_use]
37 pub fn with_epoch(epoch: Option<impl Into<OsString>>) -> Self {
38 Self {
39 epoch: epoch.map(Into::into),
40 }
41 }
42}
43
44impl Clock for SystemClock {
45 fn now(&self) -> Result<u64, StoreError> {
46 match &self.epoch {
47 Some(epoch) => epoch
48 .to_str()
49 .filter(|text| !text.is_empty() && text.bytes().all(|byte| byte.is_ascii_digit()))
50 .and_then(|text| text.parse().ok())
51 .ok_or_else(|| StoreError::Epoch(epoch.to_string_lossy().into_owned())),
52 None => Ok(SystemTime::now()
53 .duration_since(UNIX_EPOCH)
54 .map_err(|_| StoreError::Epoch("a system time before 1970".to_owned()))?
55 .as_secs()),
56 }
57 }
58}