pubmed_client/time.rs
1//! Internal time management module for cross-platform compatibility
2//!
3//! This module provides a simple time management API that works across
4//! both native and WASM targets without requiring external dependencies.
5//! It focuses on the minimal functionality needed by the PubMed client.
6
7#[cfg(not(target_arch = "wasm32"))]
8use std::time::Duration as StdDuration;
9#[cfg(not(target_arch = "wasm32"))]
10use tokio::time;
11
12/// Simple duration representation for cross-platform compatibility
13///
14/// This struct provides basic duration functionality without relying on
15/// `std::time::Duration` which is not available in WASM environments.
16#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
17pub struct Duration {
18 millis: u64,
19}
20
21impl Duration {
22 /// Create a new Duration from seconds
23 ///
24 /// # Arguments
25 ///
26 /// * `secs` - Number of seconds
27 ///
28 /// # Example
29 ///
30 /// ```
31 /// use pubmed_client::time::Duration;
32 ///
33 /// let duration = Duration::from_secs(30);
34 /// assert_eq!(duration.as_secs(), 30);
35 /// ```
36 pub fn from_secs(secs: u64) -> Self {
37 Self {
38 millis: secs * 1000,
39 }
40 }
41
42 /// Create a new Duration from milliseconds
43 ///
44 /// # Arguments
45 ///
46 /// * `millis` - Number of milliseconds
47 ///
48 /// # Example
49 ///
50 /// ```
51 /// use pubmed_client::time::Duration;
52 ///
53 /// let duration = Duration::from_millis(1500);
54 /// assert_eq!(duration.as_secs(), 1);
55 /// assert_eq!(duration.as_millis(), 1500);
56 /// ```
57 pub fn from_millis(millis: u64) -> Self {
58 Self { millis }
59 }
60
61 /// Get duration as seconds
62 ///
63 /// # Returns
64 ///
65 /// Duration in seconds as u64
66 pub fn as_secs(&self) -> u64 {
67 self.millis / 1000
68 }
69
70 /// Get duration as milliseconds
71 ///
72 /// # Returns
73 ///
74 /// Duration in milliseconds as u64
75 pub fn as_millis(&self) -> u64 {
76 self.millis
77 }
78
79 /// Get duration as seconds f64 (useful for rate calculations)
80 ///
81 /// # Returns
82 ///
83 /// Duration in seconds as f64
84 pub fn as_secs_f64(&self) -> f64 {
85 self.millis as f64 / 1000.0
86 }
87
88 /// Check if duration is zero
89 pub fn is_zero(&self) -> bool {
90 self.millis == 0
91 }
92}
93
94impl Default for Duration {
95 fn default() -> Self {
96 Self::from_secs(0)
97 }
98}
99
100impl From<u64> for Duration {
101 fn from(secs: u64) -> Self {
102 Self::from_secs(secs)
103 }
104}
105
106/// Sleep for the specified duration
107///
108/// This function provides a cross-platform sleep implementation:
109/// - On native targets: Uses tokio::time::sleep with std::time::Duration
110/// - On WASM targets: Uses a simplified implementation that returns immediately
111///
112/// # Arguments
113///
114/// * `duration` - Time to sleep
115///
116/// # Example
117///
118/// ```no_run
119/// use pubmed_client::time::{Duration, sleep};
120///
121/// #[tokio::main]
122/// async fn main() {
123/// let duration = Duration::from_secs(1);
124/// sleep(duration).await;
125/// }
126/// ```
127#[cfg(not(target_arch = "wasm32"))]
128pub async fn sleep(duration: Duration) {
129 if duration.is_zero() {
130 return;
131 }
132 time::sleep(StdDuration::from_secs(duration.as_secs())).await;
133}
134
135/// Sleep for the specified duration (WASM implementation)
136///
137/// In WASM environments, this is a simplified implementation that
138/// returns immediately. For actual delays in WASM, rate limiting
139/// should be handled by the browser's natural request scheduling.
140#[cfg(target_arch = "wasm32")]
141pub async fn sleep(_duration: Duration) {
142 // In WASM, we don't perform actual delays for rate limiting
143 // The browser will handle request scheduling naturally
144}
145
146/// Simple instant measurement for rate limiting
147///
148/// This provides basic time measurement functionality for rate limiting
149/// without requiring std::time::Instant which panics in WASM.
150#[derive(Clone, Copy, Debug)]
151pub struct Instant {
152 // For simplicity, we'll use a counter approach rather than actual time
153 _marker: (),
154}
155
156impl Instant {
157 /// Get the current instant
158 ///
159 /// Note: In WASM environments, this is a simplified implementation
160 /// that doesn't provide actual time measurement.
161 pub fn now() -> Self {
162 Self { _marker: () }
163 }
164
165 /// Calculate duration since another instant
166 ///
167 /// In WASM environments, this always returns zero duration
168 /// since we use simplified rate limiting.
169 pub fn duration_since(&self, _earlier: Instant) -> Duration {
170 Duration::from_secs(0)
171 }
172
173 /// Calculate elapsed time since this instant
174 ///
175 /// In WASM environments, this always returns zero duration.
176 pub fn elapsed(&self) -> Duration {
177 Duration::from_secs(0)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn test_duration_creation() {
187 let duration = Duration::from_secs(30);
188 assert_eq!(duration.as_secs(), 30);
189 assert_eq!(duration.as_millis(), 30000);
190 assert_eq!(duration.as_secs_f64(), 30.0);
191 }
192
193 #[test]
194 fn test_duration_from_millis() {
195 let duration = Duration::from_millis(1500);
196 assert_eq!(duration.as_secs(), 1);
197 assert_eq!(duration.as_millis(), 1500);
198 }
199
200 #[test]
201 fn test_duration_zero() {
202 let duration = Duration::default();
203 assert!(duration.is_zero());
204
205 let non_zero = Duration::from_secs(1);
206 assert!(!non_zero.is_zero());
207 }
208
209 #[test]
210 fn test_duration_ordering() {
211 let dur1 = Duration::from_secs(10);
212 let dur2 = Duration::from_secs(20);
213
214 assert!(dur1 < dur2);
215 assert!(dur2 > dur1);
216 assert_eq!(dur1, dur1);
217 }
218
219 #[test]
220 fn test_duration_from_u64() {
221 let duration: Duration = 42u64.into();
222 assert_eq!(duration.as_secs(), 42);
223 }
224
225 #[test]
226 fn test_instant_creation() {
227 let instant = Instant::now();
228 let duration = instant.elapsed();
229 assert_eq!(duration.as_secs(), 0); // In our simplified implementation
230 }
231
232 #[tokio::test]
233 async fn test_sleep_functionality() {
234 let duration = Duration::from_secs(0);
235 sleep(duration).await; // Should return immediately
236 }
237}