Skip to main content

cufflink_native/
deadline.rs

1use cufflink_types::ext::CallContext;
2use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
3
4/// A wait no call outlives, so a deadline far off stays an [`Instant`].
5const FAR_FUTURE: Duration = Duration::from_secs(86_400 * 365 * 30);
6
7/// The deadline of a call as an [`Instant`].
8pub trait CallDeadline {
9    /// When the caller stops waiting for the reply; no later than now once
10    /// it has passed.
11    fn deadline(&self) -> Instant;
12}
13
14impl CallDeadline for CallContext {
15    fn deadline(&self) -> Instant {
16        let now = SystemTime::now()
17            .duration_since(UNIX_EPOCH)
18            .unwrap_or_default();
19        Instant::now()
20            + Duration::from_millis(self.deadline_ms)
21                .saturating_sub(now)
22                .min(FAR_FUTURE)
23    }
24}
25
26#[cfg(test)]
27pub(crate) mod tests {
28    use super::*;
29    use cufflink_types::ext::CallerService;
30
31    pub(crate) fn context_until(deadline_ms: u64) -> CallContext {
32        CallContext {
33            tenant: "harbor".into(),
34            service: CallerService {
35                name: "reports-service".into(),
36                version: 3,
37            },
38            auth: None,
39            deadline_ms,
40        }
41    }
42
43    pub(crate) fn in_ms(ms: u64) -> u64 {
44        u64::try_from(
45            (SystemTime::now() + Duration::from_millis(ms))
46                .duration_since(UNIX_EPOCH)
47                .expect("clock")
48                .as_millis(),
49        )
50        .expect("millis")
51    }
52
53    #[test]
54    fn the_deadline_is_when_the_caller_stops_waiting() {
55        let before = Instant::now();
56        let deadline = context_until(in_ms(1_000)).deadline();
57        assert!(
58            deadline > before + Duration::from_millis(900)
59                && deadline <= Instant::now() + Duration::from_millis(1_000),
60            "{:?}",
61            deadline - before
62        );
63    }
64
65    #[test]
66    fn a_passed_deadline_is_now_and_a_far_one_is_bounded() {
67        assert!(context_until(0).deadline() <= Instant::now());
68        assert!(context_until(in_ms(0) - 5_000).deadline() <= Instant::now());
69        assert!(context_until(u64::MAX).deadline() <= Instant::now() + FAR_FUTURE);
70    }
71}