use std::{
cell::RefCell,
fmt::{self, Write},
ops::Deref,
rc::Rc,
time::{Duration, SystemTime},
};
use httpdate::HttpDate;
use tokio::{
task::JoinHandle,
time::{Instant, interval},
};
use crate::http::header::HeaderValue;
const DATE_VALUE_LENGTH: usize = 29;
pub trait DateTime {
const DATE_SIZE_HINT: usize = DATE_VALUE_LENGTH;
fn with_date<F, O>(&self, f: F) -> O
where
F: FnOnce(&[u8]) -> O;
fn with_date_header<F, O>(&self, f: F) -> O
where
F: FnOnce(&HeaderValue) -> 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::default()));
let state_clone = Rc::clone(&state);
let handle = tokio::task::spawn_local(async move {
let mut interval = interval(Duration::from_millis(500));
loop {
let _ = interval.tick().await;
*state_clone.borrow_mut() = DateTimeState::default();
}
});
Self { state, handle }
}
#[inline]
pub fn get(&self) -> &DateTimeHandle {
self.state.deref()
}
}
pub(crate) type DateTimeHandle = RefCell<DateTimeState>;
#[derive(Clone)]
pub struct DateTimeState {
pub date: [u8; DATE_VALUE_LENGTH],
pub date_header: HeaderValue,
pub now: Instant,
}
impl Default for DateTimeState {
fn default() -> Self {
let mut date = Self {
date: [0; DATE_VALUE_LENGTH],
date_header: HeaderValue::from_static(""),
now: Instant::now(),
};
let _ = write!(date, "{}", HttpDate::from(SystemTime::now()));
date.date_header = HeaderValue::from_bytes(&date.date).unwrap();
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 {
#[inline]
fn with_date<F, O>(&self, f: F) -> O
where
F: FnOnce(&[u8]) -> O,
{
let date = self.borrow();
f(&date.date[..])
}
#[inline]
fn with_date_header<F, O>(&self, f: F) -> O
where
F: FnOnce(&HeaderValue) -> O,
{
let date = self.borrow();
f(&date.date_header)
}
#[inline(always)]
fn now(&self) -> Instant {
self.borrow().now
}
}
pub struct SystemTimeDateTimeHandler;
impl DateTime for SystemTimeDateTimeHandler {
#[allow(dead_code)]
fn with_date<F, O>(&self, f: F) -> O
where
F: FnOnce(&[u8]) -> O,
{
let date = HttpDate::from(SystemTime::now()).to_string();
f(date.as_bytes())
}
#[allow(dead_code)]
fn with_date_header<F, O>(&self, f: F) -> O
where
F: FnOnce(&HeaderValue) -> O,
{
self.with_date(|date| {
let val = HeaderValue::from_bytes(date).unwrap();
f(&val)
})
}
fn now(&self) -> Instant {
Instant::now()
}
}