keyhog_verifier/
engine.rs1use std::future::Future;
8use std::time::{Duration, Instant};
9
10use keyhog_core::VerificationResult;
11
12use crate::verify::request::TIMEOUT_ERROR;
13
14#[derive(Debug, Clone, Copy)]
16pub struct VerificationDeadline {
17 start: Instant,
18 deadline: Instant,
19 timeout: Duration,
20}
21
22impl VerificationDeadline {
23 pub fn new(timeout: Duration) -> Self {
25 let start = Instant::now();
26 let deadline = start.checked_add(timeout).unwrap_or(start);
27 Self {
28 start,
29 deadline,
30 timeout,
31 }
32 }
33
34 pub fn for_attempts(
36 per_attempt_timeout: Duration,
37 max_attempts: usize,
38 max_backoff: Duration,
39 ) -> Self {
40 let start = Instant::now();
41 let total_duration = per_attempt_timeout
42 .saturating_mul(max_attempts as u32)
43 .saturating_add(max_backoff)
44 .max(Duration::from_secs(1));
45 let deadline = start.checked_add(total_duration).unwrap_or(start);
46 Self {
47 start,
48 deadline,
49 timeout: per_attempt_timeout,
50 }
51 }
52
53 pub fn remaining(&self) -> Result<Duration, VerificationResult> {
57 let now = Instant::now();
58 if now >= self.deadline {
59 Err(VerificationResult::Error(TIMEOUT_ERROR.into()))
60 } else {
61 Ok(self.deadline.saturating_duration_since(now))
62 }
63 }
64
65 pub fn check(&self) -> Result<(), VerificationResult> {
67 if self.is_expired() {
68 Err(VerificationResult::Error(TIMEOUT_ERROR.into()))
69 } else {
70 Ok(())
71 }
72 }
73
74 pub fn is_expired(&self) -> bool {
76 Instant::now() >= self.deadline
77 }
78
79 pub fn elapsed(&self) -> Duration {
81 self.start.elapsed()
82 }
83
84 pub fn timeout(&self) -> Duration {
86 self.timeout
87 }
88
89 pub async fn run_bounded<F, T>(&self, fut: F) -> Result<T, VerificationResult>
91 where
92 F: Future<Output = T>,
93 {
94 let remaining = self.remaining()?;
95 match tokio::time::timeout(remaining, fut).await {
96 Ok(val) => Ok(val),
97 Err(_) => Err(VerificationResult::Error(TIMEOUT_ERROR.into())),
98 }
99 }
100}