duration_ext/
lib.rs

1//! Ergonomic extension trait for creating `Duration` from numeric literals.
2//!
3//! This crate provides the [`DurationExt`] trait which extends numeric types
4//! with convenient methods for creating [`std::time::Duration`] values.
5//!
6//! # Examples
7//!
8//! ```
9//! use duration_ext::DurationExt;
10//! use std::time::Duration;
11//!
12//! // Integer literals
13//! assert_eq!(5.seconds(), Duration::from_secs(5));
14//! assert_eq!(500.millis(), Duration::from_millis(500));
15//! assert_eq!(100.micros(), Duration::from_micros(100));
16//! assert_eq!(1000.nanos(), Duration::from_nanos(1000));
17//! assert_eq!(2.minutes(), Duration::from_secs(120));
18//! assert_eq!(1.hours(), Duration::from_secs(3600));
19//!
20//! // Fractional values (f64)
21//! assert_eq!(1.5.seconds(), Duration::from_millis(1500));
22//! assert_eq!(0.5.minutes(), Duration::from_secs(30));
23//! ```
24//!
25//! # Supported Types
26//!
27//! The trait is implemented for:
28//! - `u64`, `u32`, `usize` - unsigned integers
29//! - `i32` - signed integers (negative values clamp to zero)
30//! - `f64`, `f32` - floating point (negative values clamp to zero)
31
32use std::time::Duration;
33
34/// Extension trait for creating [`Duration`] from numeric values.
35///
36/// This trait provides ergonomic methods for creating durations from
37/// numeric literals, making code more readable than using `Duration::from_*`.
38pub trait DurationExt {
39    /// Create a `Duration` representing this many nanoseconds.
40    ///
41    /// # Examples
42    ///
43    /// ```
44    /// use duration_ext::DurationExt;
45    /// use std::time::Duration;
46    ///
47    /// assert_eq!(1000.nanos(), Duration::from_nanos(1000));
48    /// assert_eq!(1.5.nanos(), Duration::from_nanos(1)); // fractional truncates
49    /// ```
50    fn nanos(self) -> Duration;
51
52    /// Create a `Duration` representing this many microseconds.
53    ///
54    /// # Examples
55    ///
56    /// ```
57    /// use duration_ext::DurationExt;
58    /// use std::time::Duration;
59    ///
60    /// assert_eq!(100.micros(), Duration::from_micros(100));
61    /// assert_eq!(1.5.micros(), Duration::from_nanos(1500));
62    /// ```
63    fn micros(self) -> Duration;
64
65    /// Create a `Duration` representing this many milliseconds.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use duration_ext::DurationExt;
71    /// use std::time::Duration;
72    ///
73    /// assert_eq!(500.millis(), Duration::from_millis(500));
74    /// assert_eq!(1.5.millis(), Duration::from_micros(1500));
75    /// ```
76    fn millis(self) -> Duration;
77
78    /// Create a `Duration` representing this many seconds.
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// use duration_ext::DurationExt;
84    /// use std::time::Duration;
85    ///
86    /// assert_eq!(5.seconds(), Duration::from_secs(5));
87    /// assert_eq!(1.5.seconds(), Duration::from_millis(1500));
88    /// ```
89    fn seconds(self) -> Duration;
90
91    /// Create a `Duration` representing this many minutes.
92    ///
93    /// # Examples
94    ///
95    /// ```
96    /// use duration_ext::DurationExt;
97    /// use std::time::Duration;
98    ///
99    /// assert_eq!(2.minutes(), Duration::from_secs(120));
100    /// assert_eq!(1.5.minutes(), Duration::from_secs(90));
101    /// ```
102    fn minutes(self) -> Duration;
103
104    /// Create a `Duration` representing this many hours.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use duration_ext::DurationExt;
110    /// use std::time::Duration;
111    ///
112    /// assert_eq!(1.hours(), Duration::from_secs(3600));
113    /// assert_eq!(1.5.hours(), Duration::from_secs(5400));
114    /// ```
115    fn hours(self) -> Duration;
116}
117
118impl DurationExt for u64 {
119    fn nanos(self) -> Duration {
120        Duration::from_nanos(self)
121    }
122
123    fn micros(self) -> Duration {
124        Duration::from_micros(self)
125    }
126
127    fn millis(self) -> Duration {
128        Duration::from_millis(self)
129    }
130
131    fn seconds(self) -> Duration {
132        Duration::from_secs(self)
133    }
134
135    fn minutes(self) -> Duration {
136        Duration::from_secs(self * 60)
137    }
138
139    fn hours(self) -> Duration {
140        Duration::from_secs(self * 3600)
141    }
142}
143
144impl DurationExt for u32 {
145    fn nanos(self) -> Duration {
146        Duration::from_nanos(self as u64)
147    }
148
149    fn micros(self) -> Duration {
150        Duration::from_micros(self as u64)
151    }
152
153    fn millis(self) -> Duration {
154        Duration::from_millis(self as u64)
155    }
156
157    fn seconds(self) -> Duration {
158        Duration::from_secs(self as u64)
159    }
160
161    fn minutes(self) -> Duration {
162        Duration::from_secs(self as u64 * 60)
163    }
164
165    fn hours(self) -> Duration {
166        Duration::from_secs(self as u64 * 3600)
167    }
168}
169
170impl DurationExt for usize {
171    fn nanos(self) -> Duration {
172        Duration::from_nanos(self as u64)
173    }
174
175    fn micros(self) -> Duration {
176        Duration::from_micros(self as u64)
177    }
178
179    fn millis(self) -> Duration {
180        Duration::from_millis(self as u64)
181    }
182
183    fn seconds(self) -> Duration {
184        Duration::from_secs(self as u64)
185    }
186
187    fn minutes(self) -> Duration {
188        Duration::from_secs(self as u64 * 60)
189    }
190
191    fn hours(self) -> Duration {
192        Duration::from_secs(self as u64 * 3600)
193    }
194}
195
196impl DurationExt for i32 {
197    fn nanos(self) -> Duration {
198        Duration::from_nanos(self.max(0) as u64)
199    }
200
201    fn micros(self) -> Duration {
202        Duration::from_micros(self.max(0) as u64)
203    }
204
205    fn millis(self) -> Duration {
206        Duration::from_millis(self.max(0) as u64)
207    }
208
209    fn seconds(self) -> Duration {
210        Duration::from_secs(self.max(0) as u64)
211    }
212
213    fn minutes(self) -> Duration {
214        Duration::from_secs(self.max(0) as u64 * 60)
215    }
216
217    fn hours(self) -> Duration {
218        Duration::from_secs(self.max(0) as u64 * 3600)
219    }
220}
221
222impl DurationExt for f64 {
223    fn nanos(self) -> Duration {
224        Duration::from_nanos(self.max(0.0) as u64)
225    }
226
227    fn micros(self) -> Duration {
228        Duration::from_secs_f64(self.max(0.0) / 1_000_000.0)
229    }
230
231    fn millis(self) -> Duration {
232        Duration::from_secs_f64(self.max(0.0) / 1000.0)
233    }
234
235    fn seconds(self) -> Duration {
236        Duration::from_secs_f64(self.max(0.0))
237    }
238
239    fn minutes(self) -> Duration {
240        Duration::from_secs_f64(self.max(0.0) * 60.0)
241    }
242
243    fn hours(self) -> Duration {
244        Duration::from_secs_f64(self.max(0.0) * 3600.0)
245    }
246}
247
248impl DurationExt for f32 {
249    fn nanos(self) -> Duration {
250        Duration::from_nanos(self.max(0.0) as u64)
251    }
252
253    fn micros(self) -> Duration {
254        Duration::from_secs_f32(self.max(0.0) / 1_000_000.0)
255    }
256
257    fn millis(self) -> Duration {
258        Duration::from_secs_f32(self.max(0.0) / 1000.0)
259    }
260
261    fn seconds(self) -> Duration {
262        Duration::from_secs_f32(self.max(0.0))
263    }
264
265    fn minutes(self) -> Duration {
266        Duration::from_secs_f32(self.max(0.0) * 60.0)
267    }
268
269    fn hours(self) -> Duration {
270        Duration::from_secs_f32(self.max(0.0) * 3600.0)
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277
278    // ========== u64 tests ==========
279
280    #[test]
281    fn test_u64_nanos() {
282        assert_eq!(1000u64.nanos(), Duration::from_nanos(1000));
283    }
284
285    #[test]
286    fn test_u64_micros() {
287        assert_eq!(100u64.micros(), Duration::from_micros(100));
288    }
289
290    #[test]
291    fn test_u64_millis() {
292        assert_eq!(500u64.millis(), Duration::from_millis(500));
293    }
294
295    #[test]
296    fn test_u64_seconds() {
297        assert_eq!(2u64.seconds(), Duration::from_secs(2));
298    }
299
300    #[test]
301    fn test_u64_minutes() {
302        assert_eq!(2u64.minutes(), Duration::from_secs(120));
303    }
304
305    #[test]
306    fn test_u64_hours() {
307        assert_eq!(2u64.hours(), Duration::from_secs(7200));
308    }
309
310    // ========== u32 tests ==========
311
312    #[test]
313    fn test_u32_seconds() {
314        assert_eq!(3u32.seconds(), Duration::from_secs(3));
315    }
316
317    #[test]
318    fn test_u32_minutes() {
319        assert_eq!(5u32.minutes(), Duration::from_secs(300));
320    }
321
322    #[test]
323    fn test_u32_hours() {
324        assert_eq!(1u32.hours(), Duration::from_secs(3600));
325    }
326
327    // ========== usize tests ==========
328
329    #[test]
330    fn test_usize_seconds() {
331        assert_eq!(5usize.seconds(), Duration::from_secs(5));
332    }
333
334    #[test]
335    fn test_usize_minutes() {
336        assert_eq!(10usize.minutes(), Duration::from_secs(600));
337    }
338
339    // ========== i32 tests ==========
340
341    #[test]
342    fn test_i32_seconds() {
343        assert_eq!(4i32.seconds(), Duration::from_secs(4));
344    }
345
346    #[test]
347    fn test_i32_negative_clamps_to_zero() {
348        assert_eq!((-1i32).seconds(), Duration::from_secs(0));
349        assert_eq!((-100i32).millis(), Duration::from_millis(0));
350        assert_eq!((-5i32).minutes(), Duration::from_secs(0));
351    }
352
353    // ========== f64 tests ==========
354
355    #[test]
356    fn test_f64_seconds() {
357        assert_eq!(1.5f64.seconds(), Duration::from_secs_f64(1.5));
358    }
359
360    #[test]
361    fn test_f64_fractional_seconds_equals_millis() {
362        // The specific test requested: 1.5.seconds() == 1500ms
363        assert_eq!(1.5.seconds(), Duration::from_millis(1500));
364    }
365
366    #[test]
367    fn test_f64_millis() {
368        assert_eq!(1.5f64.millis(), Duration::from_micros(1500));
369    }
370
371    #[test]
372    fn test_f64_micros() {
373        assert_eq!(1.5f64.micros(), Duration::from_nanos(1500));
374    }
375
376    #[test]
377    fn test_f64_minutes() {
378        assert_eq!(1.5f64.minutes(), Duration::from_secs(90));
379        assert_eq!(0.5f64.minutes(), Duration::from_secs(30));
380    }
381
382    #[test]
383    fn test_f64_hours() {
384        assert_eq!(1.5f64.hours(), Duration::from_secs(5400));
385        assert_eq!(0.5f64.hours(), Duration::from_secs(1800));
386    }
387
388    #[test]
389    fn test_f64_negative_clamps_to_zero() {
390        assert_eq!((-1.5f64).seconds(), Duration::from_secs(0));
391        assert_eq!((-100.0f64).millis(), Duration::from_millis(0));
392    }
393
394    // ========== f32 tests ==========
395
396    #[test]
397    fn test_f32_seconds() {
398        assert_eq!(1.5f32.seconds(), Duration::from_secs_f32(1.5));
399    }
400
401    #[test]
402    fn test_f32_fractional_seconds_equals_millis() {
403        // Same test for f32
404        assert_eq!(1.5f32.seconds(), Duration::from_millis(1500));
405    }
406
407    #[test]
408    fn test_f32_minutes() {
409        assert_eq!(2.5f32.minutes(), Duration::from_secs(150));
410    }
411
412    // ========== Edge cases ==========
413
414    #[test]
415    fn test_zero_duration() {
416        assert_eq!(0u64.seconds(), Duration::ZERO);
417        assert_eq!(0.0f64.seconds(), Duration::ZERO);
418        assert_eq!(0u32.minutes(), Duration::ZERO);
419    }
420
421    #[test]
422    fn test_large_values() {
423        // 24 hours in seconds
424        assert_eq!(24u64.hours(), Duration::from_secs(86400));
425        // 1 week in hours
426        assert_eq!(168u64.hours(), Duration::from_secs(604800));
427    }
428
429    #[test]
430    fn test_small_fractional_values() {
431        // Very small durations
432        assert_eq!(0.001f64.seconds(), Duration::from_millis(1));
433        assert_eq!(0.000001f64.seconds(), Duration::from_micros(1));
434    }
435
436    // ========== Practical usage examples ==========
437
438    #[test]
439    fn test_common_animation_durations() {
440        // Common animation timing values
441        assert_eq!(0.3.seconds(), Duration::from_millis(300));
442        assert_eq!(0.25.seconds(), Duration::from_millis(250));
443        assert_eq!(0.5.seconds(), Duration::from_millis(500));
444        assert_eq!(1.0.seconds(), Duration::from_secs(1));
445    }
446
447    #[test]
448    fn test_timeout_durations() {
449        // Common timeout values
450        assert_eq!(30.seconds(), Duration::from_secs(30));
451        assert_eq!(5.minutes(), Duration::from_secs(300));
452        assert_eq!(1.hours(), Duration::from_secs(3600));
453    }
454}