use std::{
cell::RefCell,
fmt::{self, Write},
ops::Deref,
rc::Rc,
time::{Duration, SystemTime},
};
use httpdate::HttpDate;
use tokio::{
task::JoinHandle,
time::{interval, Instant},
};
pub trait DateTime {
const DATE_VALUE_LENGTH: usize;
fn with_date<F, O>(&self, f: F) -> O
where
F: FnOnce(&[u8]) -> O;
fn now(&self) -> Instant;
}
pub struct DateTimeService {
state: Rc<RefCell<DateTimeState>>,
handle: JoinHandle<()>,
}
impl Drop for DateTimeService {
fn drop(&mut self) {
self.handle.abort();
}
}
impl Default for DateTimeService {
fn default() -> Self {
Self::new()
}
}
impl DateTimeService {
pub fn new() -> Self {
let state = Rc::new(RefCell::new(DateTimeState::new()));
let state_clone = Rc::clone(&state);
let handle = tokio::task::spawn_local(async move {
let mut interval = interval(Duration::from_millis(500));
let state = &*state_clone;
loop {
let _ = interval.tick().await;
*state.borrow_mut() = DateTimeState::new();
}
});
Self { state, handle }
}
#[inline]
pub fn get(&self) -> &DateTimeHandle {
self.state.deref()
}
}
pub(crate) type DateTimeHandle = RefCell<DateTimeState>;
pub const DATE_VALUE_LENGTH: usize = 29;
#[derive(Copy, Clone)]
pub struct DateTimeState {
pub date: [u8; DATE_VALUE_LENGTH],
pub now: Instant,
}
impl Default for DateTimeState {
fn default() -> Self {
Self::new()
}
}
impl DateTimeState {
pub fn new() -> Self {
let mut date = Self {
date: [0; DATE_VALUE_LENGTH],
now: Instant::now(),
};
let _ = write!(date, "{}", HttpDate::from(SystemTime::now()));
date
}
}
impl Write for DateTimeState {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.date[..].copy_from_slice(s.as_bytes());
Ok(())
}
}
impl DateTime for DateTimeHandle {
const DATE_VALUE_LENGTH: usize = DATE_VALUE_LENGTH;
#[allow(dead_code)]
#[inline]
fn with_date<F, O>(&self, f: F) -> O
where
F: FnOnce(&[u8]) -> O,
{
let date = self.borrow();
f(&date.date[..])
}
#[inline(always)]
fn now(&self) -> Instant {
self.borrow().now
}
}