scrappy_utils/
time.rs

1use std::convert::Infallible;
2use std::task::{Context, Poll};
3use std::time::{self, Duration, Instant};
4
5use scrappy_rt::time::delay_for;
6use scrappy_service::{Service, ServiceFactory};
7use futures::future::{ok, ready, FutureExt, Ready};
8
9use super::cell::Cell;
10
11#[derive(Clone, Debug)]
12pub struct LowResTime(Cell<Inner>);
13
14#[derive(Debug)]
15struct Inner {
16    resolution: Duration,
17    current: Option<Instant>,
18}
19
20impl Inner {
21    fn new(resolution: Duration) -> Self {
22        Inner {
23            resolution,
24            current: None,
25        }
26    }
27}
28
29impl LowResTime {
30    pub fn with(resolution: Duration) -> LowResTime {
31        LowResTime(Cell::new(Inner::new(resolution)))
32    }
33
34    pub fn timer(&self) -> LowResTimeService {
35        LowResTimeService(self.0.clone())
36    }
37}
38
39impl Default for LowResTime {
40    fn default() -> Self {
41        LowResTime(Cell::new(Inner::new(Duration::from_secs(1))))
42    }
43}
44
45impl ServiceFactory for LowResTime {
46    type Request = ();
47    type Response = Instant;
48    type Error = Infallible;
49    type InitError = Infallible;
50    type Config = ();
51    type Service = LowResTimeService;
52    type Future = Ready<Result<Self::Service, Self::InitError>>;
53
54    fn new_service(&self, _: ()) -> Self::Future {
55        ok(self.timer())
56    }
57}
58
59#[derive(Clone, Debug)]
60pub struct LowResTimeService(Cell<Inner>);
61
62impl LowResTimeService {
63    pub fn with(resolution: Duration) -> LowResTimeService {
64        LowResTimeService(Cell::new(Inner::new(resolution)))
65    }
66
67    /// Get current time. This function has to be called from
68    /// future's poll method, otherwise it panics.
69    pub fn now(&self) -> Instant {
70        let cur = self.0.get_ref().current;
71        if let Some(cur) = cur {
72            cur
73        } else {
74            let now = Instant::now();
75            let mut inner = self.0.clone();
76            let interval = {
77                let mut b = inner.get_mut();
78                b.current = Some(now);
79                b.resolution
80            };
81
82            scrappy_rt::spawn(delay_for(interval).then(move |_| {
83                inner.get_mut().current.take();
84                ready(())
85            }));
86            now
87        }
88    }
89}
90
91impl Service for LowResTimeService {
92    type Request = ();
93    type Response = Instant;
94    type Error = Infallible;
95    type Future = Ready<Result<Self::Response, Self::Error>>;
96
97    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
98        Poll::Ready(Ok(()))
99    }
100
101    fn call(&mut self, _: ()) -> Self::Future {
102        ok(self.now())
103    }
104}
105
106#[derive(Clone, Debug)]
107pub struct SystemTime(Cell<SystemTimeInner>);
108
109#[derive(Debug)]
110struct SystemTimeInner {
111    resolution: Duration,
112    current: Option<time::SystemTime>,
113}
114
115impl SystemTimeInner {
116    fn new(resolution: Duration) -> Self {
117        SystemTimeInner {
118            resolution,
119            current: None,
120        }
121    }
122}
123
124#[derive(Clone, Debug)]
125pub struct SystemTimeService(Cell<SystemTimeInner>);
126
127impl SystemTimeService {
128    pub fn with(resolution: Duration) -> SystemTimeService {
129        SystemTimeService(Cell::new(SystemTimeInner::new(resolution)))
130    }
131
132    /// Get current time. This function has to be called from
133    /// future's poll method, otherwise it panics.
134    pub fn now(&self) -> time::SystemTime {
135        let cur = self.0.get_ref().current;
136        if let Some(cur) = cur {
137            cur
138        } else {
139            let now = time::SystemTime::now();
140            let mut inner = self.0.clone();
141            let interval = {
142                let mut b = inner.get_mut();
143                b.current = Some(now);
144                b.resolution
145            };
146
147            scrappy_rt::spawn(delay_for(interval).then(move |_| {
148                inner.get_mut().current.take();
149                ready(())
150            }));
151            now
152        }
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use std::time::{Duration, SystemTime};
160
161    /// State Under Test: Two calls of `SystemTimeService::now()` return the same value if they are done within resolution interval of `SystemTimeService`.
162    ///
163    /// Expected Behavior: Two back-to-back calls of `SystemTimeService::now()` return the same value.
164    #[scrappy_rt::test]
165    async fn system_time_service_time_does_not_immediately_change() {
166        let resolution = Duration::from_millis(50);
167
168        let time_service = SystemTimeService::with(resolution);
169        assert_eq!(time_service.now(), time_service.now());
170    }
171
172    /// State Under Test: Two calls of `LowResTimeService::now()` return the same value if they are done within resolution interval of `SystemTimeService`.
173    ///
174    /// Expected Behavior: Two back-to-back calls of `LowResTimeService::now()` return the same value.
175    #[scrappy_rt::test]
176    async fn lowres_time_service_time_does_not_immediately_change() {
177        let resolution = Duration::from_millis(50);
178        let time_service = LowResTimeService::with(resolution);
179        assert_eq!(time_service.now(), time_service.now());
180    }
181
182    /// State Under Test: `SystemTimeService::now()` updates returned value every resolution period.
183    ///
184    /// Expected Behavior: Two calls of `LowResTimeService::now()` made in subsequent resolution interval return different values
185    /// and second value is greater than the first one at least by a resolution interval.
186    #[scrappy_rt::test]
187    async fn system_time_service_time_updates_after_resolution_interval() {
188        let resolution = Duration::from_millis(100);
189        let wait_time = Duration::from_millis(300);
190
191        let time_service = SystemTimeService::with(resolution);
192
193        let first_time = time_service
194            .now()
195            .duration_since(SystemTime::UNIX_EPOCH)
196            .unwrap();
197
198        delay_for(wait_time).await;
199
200        let second_time = time_service
201            .now()
202            .duration_since(SystemTime::UNIX_EPOCH)
203            .unwrap();
204
205        assert!(second_time - first_time >= wait_time);
206    }
207
208    /// State Under Test: `LowResTimeService::now()` updates returned value every resolution period.
209    ///
210    /// Expected Behavior: Two calls of `LowResTimeService::now()` made in subsequent resolution interval return different values
211    /// and second value is greater than the first one at least by a resolution interval.
212    #[scrappy_rt::test]
213    async fn lowres_time_service_time_updates_after_resolution_interval() {
214        let resolution = Duration::from_millis(100);
215        let wait_time = Duration::from_millis(300);
216        let time_service = LowResTimeService::with(resolution);
217
218        let first_time = time_service.now();
219
220        delay_for(wait_time).await;
221
222        let second_time = time_service.now();
223        assert!(second_time - first_time >= wait_time);
224    }
225}