1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! low resolution async date time for reduced syscall for generating http date time.

use std::{
    cell::RefCell,
    fmt::{self, Write},
    ops::Deref,
    rc::Rc,
    time::{Duration, SystemTime},
};

use httpdate::HttpDate;
use tokio::{
    task::JoinHandle,
    time::{interval, Instant},
};

/// Trait for getting current date/time.
///
/// This is usually used by a low resolution of timer to reduce frequent syscall to OS.
pub trait DateTime {
    /// The size hint of slice by Self::date method.
    const DATE_VALUE_LENGTH: usize;

    /// closure would receive byte slice representation of [HttpDate].
    fn with_date<F, O>(&self, f: F) -> O
    where
        F: FnOnce(&[u8]) -> O;

    fn now(&self) -> Instant;
}

/// Struct with Date update periodically at 500 milli seconds interval.
pub struct DateTimeService {
    state: Rc<RefCell<DateTimeState>>,
    handle: JoinHandle<()>,
}

impl Drop for DateTimeService {
    fn drop(&mut self) {
        // stop the timer update async task on drop.
        self.handle.abort();
    }
}

impl Default for DateTimeService {
    fn default() -> Self {
        Self::new()
    }
}

impl DateTimeService {
    pub fn new() -> Self {
        // shared date and timer for Date and update async task.
        let state = Rc::new(RefCell::new(DateTimeState::new()));
        let state_clone = Rc::clone(&state);
        // spawn an async task sleep for 1 sec and update date in a loop.
        // handle is used to stop the task on Date drop.
        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>;

/// The length of byte representation of [HttpDate].
pub const DATE_VALUE_LENGTH: usize = 29;

/// struct contains byte representation of [HttpDate] and [Instant].
#[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;

    // TODO: remove this allow
    #[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
    }
}