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}