use crate::timers::WallClockTimer;
use snafu::prelude::*;
use std::time::Duration;
#[derive(Debug, Snafu)]
pub enum ChronoTimerError<E>
where
E: std::error::Error + Send + Sync + 'static,
{
#[snafu(display("chrono::DateTime is out of range for SystemTime"))]
ChronoOutOfRange,
#[snafu(display("timer error while scheduling chrono DateTime: {source}"))]
ImplementationSpecific { source: E },
}
pub trait ChronoTimer: WallClockTimer {
fn schedule_at_datetime<Tz>(
&mut self,
deadline: chrono::DateTime<Tz>,
state: Self::State,
) -> Result<(), ChronoTimerError<Self::Error>>
where
Tz: chrono::TimeZone,
Tz::Offset: Send + Sync;
}
impl<T> ChronoTimer for T
where
T: WallClockTimer,
{
fn schedule_at_datetime<Tz>(
&mut self,
deadline: chrono::DateTime<Tz>,
state: Self::State,
) -> Result<(), ChronoTimerError<Self::Error>>
where
Tz: chrono::TimeZone,
Tz::Offset: Send + Sync,
{
let delta = deadline
.with_timezone(&chrono::Utc)
.signed_duration_since(chrono::DateTime::<chrono::Utc>::UNIX_EPOCH);
let duration = delta.to_std().ok().context(ChronoOutOfRangeSnafu)?;
let deadline = std::time::UNIX_EPOCH
.checked_add(Duration::from_secs(duration.as_secs()))
.and_then(|t| t.checked_add(Duration::from_nanos(duration.subsec_nanos() as u64)))
.context(ChronoOutOfRangeSnafu)?;
self.schedule_at(deadline, state)
.context(ImplementationSpecificSnafu)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::timers::{State, WallClockTimer};
#[derive(Debug)]
struct TestState {
id: u64,
}
impl State for TestState {
type Id = u64;
fn id(&self) -> &Self::Id {
&self.id
}
fn trigger(self) {}
}
#[derive(Debug)]
struct StubTimer {
last_deadline: Option<std::time::SystemTime>,
}
#[derive(Debug)]
struct StubError;
impl std::fmt::Display for StubError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "stub")
}
}
impl std::error::Error for StubError {}
impl WallClockTimer for StubTimer {
type Id = u64;
type State = TestState;
type Error = StubError;
fn schedule_at(
&mut self,
deadline: std::time::SystemTime,
_state: Self::State,
) -> Result<(), Self::Error> {
self.last_deadline = Some(deadline);
Ok(())
}
fn cancel(&mut self, _id: Self::Id) -> Result<(), Self::Error> {
Ok(())
}
}
#[test]
fn chrono_datetime_converts_to_systemtime() {
let mut timer = StubTimer {
last_deadline: None,
};
let dt = chrono::DateTime::<chrono::Utc>::from_timestamp(1, 0).expect("timestamp");
timer
.schedule_at_datetime(dt, TestState { id: 1 })
.expect("schedule");
let expected = std::time::UNIX_EPOCH + std::time::Duration::from_secs(1);
assert_eq!(timer.last_deadline, Some(expected));
}
}