duration_extender/
lib.rs

1use std::time::Duration;
2
3/// An extension trait that adds fluent time unit methods to integer primitives,
4/// allowing for highly readable time duration creation.
5/// 
6/// The functions return a standard Rust `std::time::Duration`.
7pub trait DurationExt {
8    /// Creates a Duration representing this many seconds.
9    fn seconds(self) -> Duration;
10    /// Creates a Duration representing this many minutes.
11    fn minutes(self) -> Duration;
12    /// Creates a Duration representing this many hours.
13    fn hours(self) -> Duration;
14    /// Creates a Duration representing this many days (24 hours).
15    fn days(self) -> Duration;
16    /// Creates a Duration representing this many weeks (7 days).
17    fn weeks(self) -> Duration;
18    /// Creates a Duration representing this many milliseconds.
19    fn milliseconds(self) -> Duration;
20    /// Creates a Duration representing this many microseconds.
21    fn microseconds(self) -> Duration;
22    /// Creates a Duration representing this many nanoseconds.
23    fn nanoseconds(self) -> Duration;
24}
25
26impl DurationExt for u64 {
27    fn seconds(self) -> Duration {
28        Duration::from_secs(self)
29    }
30
31    fn minutes(self) -> Duration {
32        Duration::from_secs(self.saturating_mul(60))
33    }
34
35    fn hours(self) -> Duration {
36        Duration::from_secs(self.saturating_mul(3600))
37    }
38
39    fn days(self) -> Duration {
40        Duration::from_secs(self.saturating_mul(86400))
41    }
42
43    fn weeks(self) -> Duration {
44        Duration::from_secs(self.saturating_mul(604800))
45    }
46    
47    fn milliseconds(self) -> Duration {
48        Duration::from_millis(self)
49    }
50
51    fn microseconds(self) -> Duration {
52        Duration::from_micros(self)
53    }
54
55    fn nanoseconds(self) -> Duration {
56        Duration::from_nanos(self)
57    }
58}
59
60impl DurationExt for u32 {
61    fn seconds(self) -> Duration {
62        Duration::from_secs(self as u64)
63    }
64
65    fn minutes(self) -> Duration {
66        Duration::from_secs((self as u64).saturating_mul(60))
67    }
68
69    fn hours(self) -> Duration {
70        Duration::from_secs((self as u64).saturating_mul(3600))
71    }
72
73    fn days(self) -> Duration {
74        Duration::from_secs((self as u64).saturating_mul(86400))
75    }
76
77    fn weeks(self) -> Duration {
78        Duration::from_secs((self as u64).saturating_mul(604800))
79    }
80
81    fn milliseconds(self) -> Duration {
82        Duration::from_millis(self as u64)
83    }
84
85    fn microseconds(self) -> Duration {
86        Duration::from_micros(self as u64)
87    }
88
89    fn nanoseconds(self) -> Duration {
90        Duration::from_nanos(self as u64)
91    }
92}
93
94impl DurationExt for i64 {
95    fn seconds(self) -> Duration {
96        assert!(self >= 0, "duration cannot be negative: got {} seconds", self);
97        Duration::from_secs(self as u64)
98    }
99
100    fn minutes(self) -> Duration {
101        assert!(self >= 0, "duration cannot be negative: got {} minutes", self);
102                                                   
103        Duration::from_secs((self as u64).saturating_mul(60))
104    }
105
106    fn hours(self) -> Duration {
107        assert!(self >= 0, "duration cannot be negative: got {} hours", self);
108                                                         
109        Duration::from_secs((self as u64).saturating_mul(3600))
110    }
111
112    fn days(self) -> Duration {
113        assert!(self >= 0, "duration cannot be negative: got {} days", self);
114                                                           
115        Duration::from_secs((self as u64).saturating_mul(86400))
116    }
117
118    fn weeks(self) -> Duration {
119        assert!(self >= 0, "duration cannot be negative: got {} weeks", self);
120                                                            
121        Duration::from_secs((self as u64).saturating_mul(604800))
122    }
123    
124    fn milliseconds(self) -> Duration {
125        assert!(self >= 0, "duration cannot be negative: got {} milliseconds", self);
126                                                    
127        Duration::from_millis(self as u64)
128    }
129
130    fn microseconds(self) -> Duration {
131        assert!(self >= 0, "duration cannot be negative: got {} microseconds", self);
132                                                 
133        Duration::from_micros(self as u64)
134    }
135
136    fn nanoseconds(self) -> Duration {
137        assert!(self >= 0, "duration cannot be negative: got {} nanoseconds", self);                                                    
138        Duration::from_nanos(self as u64)
139    }
140}
141
142impl DurationExt for i32 {
143    fn seconds(self) -> Duration {
144        assert!(self >= 0, "duration cannot be negative: got {} seconds", self);
145        Duration::from_secs(self as u64)
146    }
147
148    fn minutes(self) -> Duration {
149        assert!(self >= 0, "duration cannot be negative: got {} minutes", self);
150        Duration::from_secs((self as u64).saturating_mul(60))
151    }
152
153    fn hours(self) -> Duration {
154        assert!(self >= 0, "duration cannot be negative: got {} hours", self);
155        Duration::from_secs((self as u64).saturating_mul(3600))
156    }
157
158    fn days(self) -> Duration {
159        assert!(self >= 0, "duration cannot be negative: got {} days", self);
160        Duration::from_secs((self as u64).saturating_mul(86400))
161    }
162
163    fn weeks(self) -> Duration {
164        assert!(self >= 0, "duration cannot be negative: got {} weeks", self);
165        Duration::from_secs((self as u64).saturating_mul(604800))
166    }
167
168    fn milliseconds(self) -> Duration {
169        assert!(self >= 0, "duration cannot be negative: got {} milliseconds", self);
170        Duration::from_millis(self as u64)
171    }
172
173    fn microseconds(self) -> Duration {
174        assert!(self >= 0, "duration cannot be negative: got {} microseconds", self);
175        Duration::from_micros(self as u64)
176    }
177
178    fn nanoseconds(self) -> Duration {
179        assert!(self >= 0, "duration cannot be negative: got {} nanoseconds", self);
180        Duration::from_nanos(self as u64)
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use std::time::Duration;
188
189    // --- U64 Tests ---
190    #[test]
191    fn test_u64_large_units() {
192        let five: u64 = 5;
193        assert_eq!(five.seconds(), Duration::from_secs(5));
194        assert_eq!(five.minutes(), Duration::from_secs(5 * 60));
195        assert_eq!(five.hours(), Duration::from_secs(5 * 3600));
196        assert_eq!(five.days(), Duration::from_secs(5 * 86400));
197        assert_eq!(five.weeks(), Duration::from_secs(5 * 604800));
198    }
199
200    #[test]
201    fn test_u64_small_units() {
202        let one: u64 = 1;
203        assert_eq!(one.milliseconds(), Duration::from_millis(1));
204        assert_eq!(one.microseconds(), Duration::from_micros(1));
205        assert_eq!(one.nanoseconds(), Duration::from_nanos(1));
206
207        let large_num: u64 = 1_234_567;
208        assert_eq!(large_num.milliseconds(), Duration::from_millis(1_234_567));
209    }
210
211    // --- U32 Tests ---
212    #[test]
213    fn test_u32_large_units() {
214        let three: u32 = 3;
215        assert_eq!(three.seconds(), Duration::from_secs(3));
216        assert_eq!(three.minutes(), Duration::from_secs(3 * 60));
217        assert_eq!(three.hours(), Duration::from_secs(3 * 3600));
218    }
219
220    #[test]
221    fn test_u32_small_units() {
222        let num: u32 = 999;
223        assert_eq!(num.milliseconds(), Duration::from_millis(999));
224        assert_eq!(num.microseconds(), Duration::from_micros(999));
225        assert_eq!(num.nanoseconds(), Duration::from_nanos(999));
226    }
227
228    // --- I64 Tests ---
229    #[test]
230fn test_i64_positive() {
231    let pos_ten: i64 = 10;
232    assert_eq!(pos_ten.minutes(), Duration::from_secs(600));
233    assert_eq!(pos_ten.milliseconds(), Duration::from_millis(10));
234}
235
236#[test]
237#[should_panic(expected = "duration cannot be negative")]
238fn test_i64_negative_panics() {
239    let neg_ten: i64 = -10;
240    let _ = neg_ten.minutes(); 
241}
242
243#[test]
244fn test_i64_zero() {
245    let zero: i64 = 0;
246    assert_eq!(zero.seconds(), Duration::ZERO); 
247}
248
249// --- I32 Tests ---
250#[test]
251fn test_i32_positive() {
252    let pos_two: i32 = 2;
253    assert_eq!(pos_two.days(), Duration::from_secs(2 * 86400));
254    assert_eq!(pos_two.microseconds(), Duration::from_micros(2));
255}
256
257#[test]
258#[should_panic(expected = "duration cannot be negative")]
259fn test_i32_negative_panics() {
260    let neg_two: i32 = -2;
261    let _ = neg_two.days(); 
262}
263
264#[test]
265fn test_i32_zero() {
266    let zero: i32 = 0;
267    assert_eq!(zero.seconds(), Duration::ZERO); 
268}
269    // --- Saturation Test ---
270    #[test]
271    fn test_saturating_mul_no_panic() {
272        let max_u64 = u64::MAX;
273        // Test large multiplication for saturation
274        let duration = max_u64.minutes();
275        assert!(duration.as_secs() > 0); 
276        
277        // Test small multiplication where the value is simply passed to std::time::Duration 
278        // (i.e., we rely on Duration::from_* to handle the conversion/saturation internally, 
279        // but ensure our methods don't panic)
280        let small_duration = max_u64.milliseconds();
281        assert!(small_duration.as_millis() > 0);
282    }
283}