1use core::{marker::PhantomData, time::Duration};
14
15use num_traits::cast::{NumCast, ToPrimitive};
16
17use crate::{ByteRate, ByteSize, Frequency, Ratio, Time};
18
19fn round_i64(value: f64) -> i64 {
21 if value.is_nan() {
22 return 0;
23 }
24 let rounded = value.round();
25 rounded
26 .to_i64()
27 .unwrap_or(if rounded < 0.0 { i64::MIN } else { i64::MAX })
28}
29
30fn round_i32(value: f64) -> i32 {
32 if value.is_nan() {
33 return 0;
34 }
35 let rounded = value.round();
36 rounded
37 .to_i32()
38 .unwrap_or(if rounded < 0.0 { i32::MIN } else { i32::MAX })
39}
40
41fn round_u64(value: f64) -> u64 {
44 if value.is_nan() {
45 return 0;
46 }
47 let rounded = value.round();
48 rounded
49 .to_u64()
50 .unwrap_or(if rounded < 0.0 { 0 } else { u64::MAX })
51}
52
53fn round_usize(value: f64) -> usize {
56 if value.is_nan() {
57 return 0;
58 }
59 let rounded = value.round();
60 rounded
61 .to_usize()
62 .unwrap_or(if rounded < 0.0 { 0 } else { usize::MAX })
63}
64
65fn widen<T: ToPrimitive>(value: T) -> f64 {
67 NumCast::from(value).unwrap_or(f64::NAN)
68}
69
70pub trait ByteSizeExt: Sized + Copy {
72 const ZERO: Self;
74
75 fn from_bytes(bytes: u64) -> Self;
77
78 fn from_bytes_i64(bytes: i64) -> Self;
83
84 fn from_bytes_f64(bytes: f64) -> Self;
86
87 fn bytes_u64(self) -> u64;
89
90 fn bytes_usize(self) -> usize;
92
93 fn bytes_i32(self) -> i32;
95
96 fn bytes_i64(self) -> i64;
98
99 fn bytes_f64(self) -> f64;
101}
102
103impl ByteSizeExt for ByteSize {
104 const ZERO: Self = Self {
105 dimension: PhantomData,
106 units: PhantomData,
107 value: 0.0,
108 };
109
110 fn from_bytes(bytes: u64) -> Self {
111 Self::from_bytes_f64(widen(bytes))
112 }
113
114 fn from_bytes_i64(bytes: i64) -> Self {
115 Self::from_bytes_f64(widen(bytes))
116 }
117
118 fn from_bytes_f64(bytes: f64) -> Self {
119 Self {
120 dimension: PhantomData,
121 units: PhantomData,
122 value: bytes,
123 }
124 }
125
126 fn bytes_u64(self) -> u64 {
127 round_u64(self.value)
128 }
129
130 fn bytes_usize(self) -> usize {
131 round_usize(self.value)
132 }
133
134 fn bytes_i32(self) -> i32 {
135 round_i32(self.value)
136 }
137
138 fn bytes_i64(self) -> i64 {
139 round_i64(self.value)
140 }
141
142 fn bytes_f64(self) -> f64 {
143 self.value
144 }
145}
146
147pub trait TimeExt: Sized + Copy {
150 const ZERO: Self;
152
153 fn from_millis(millis: i64) -> Self;
156
157 fn from_micros(micros: i64) -> Self;
159
160 fn from_nanos(nanos: i64) -> Self;
162
163 fn from_secs(secs: i64) -> Self;
165
166 fn from_secs_f64(secs: f64) -> Self;
168
169 fn from_std(duration: Duration) -> Self;
171
172 fn millis_i32(self) -> i32;
174
175 fn millis_i64(self) -> i64;
177
178 fn millis_i64_trunc(self) -> i64;
186
187 fn micros_i64(self) -> i64;
189
190 fn nanos_i64(self) -> i64;
192
193 fn secs_i64(self) -> i64;
195
196 fn secs_f64(self) -> f64;
198
199 fn to_std(self) -> Duration;
205}
206
207impl TimeExt for Time {
208 const ZERO: Self = Self {
209 dimension: PhantomData,
210 units: PhantomData,
211 value: 0.0,
212 };
213
214 fn from_millis(millis: i64) -> Self {
215 Self::from_secs_f64(widen(millis) / 1e3)
216 }
217
218 fn from_micros(micros: i64) -> Self {
219 Self::from_secs_f64(widen(micros) / 1e6)
220 }
221
222 fn from_nanos(nanos: i64) -> Self {
223 Self::from_secs_f64(widen(nanos) / 1e9)
224 }
225
226 fn from_secs(secs: i64) -> Self {
227 Self::from_secs_f64(widen(secs))
228 }
229
230 fn from_secs_f64(secs: f64) -> Self {
231 Self {
232 dimension: PhantomData,
233 units: PhantomData,
234 value: secs,
235 }
236 }
237
238 fn from_std(duration: Duration) -> Self {
239 Self::from_secs_f64(duration.as_secs_f64())
240 }
241
242 fn millis_i32(self) -> i32 {
243 round_i32(self.value * 1e3)
244 }
245
246 fn millis_i64(self) -> i64 {
247 round_i64(self.value * 1e3)
248 }
249
250 fn millis_i64_trunc(self) -> i64 {
251 self.nanos_i64() / 1_000_000
255 }
256
257 fn micros_i64(self) -> i64 {
258 round_i64(self.value * 1e6)
259 }
260
261 fn nanos_i64(self) -> i64 {
262 round_i64(self.value * 1e9)
263 }
264
265 fn secs_i64(self) -> i64 {
266 round_i64(self.value)
267 }
268
269 fn secs_f64(self) -> f64 {
270 self.value
271 }
272
273 fn to_std(self) -> Duration {
274 Duration::try_from_secs_f64(self.value).unwrap_or(Duration::ZERO)
275 }
276}
277
278pub trait ByteRateExt: Sized + Copy {
280 const ZERO: Self;
282
283 fn from_bytes_per_sec(rate: i64) -> Self;
285
286 fn from_bytes_per_sec_f64(rate: f64) -> Self;
288
289 fn bytes_per_sec_f64(self) -> f64;
291
292 fn bytes_per_sec_i64(self) -> i64;
294
295 fn time_to_transfer(self, size: ByteSize) -> Time;
300}
301
302impl ByteRateExt for ByteRate {
303 const ZERO: Self = Self {
304 dimension: PhantomData,
305 units: PhantomData,
306 value: 0.0,
307 };
308
309 fn from_bytes_per_sec(rate: i64) -> Self {
310 Self::from_bytes_per_sec_f64(widen(rate))
311 }
312
313 fn from_bytes_per_sec_f64(rate: f64) -> Self {
314 Self {
315 dimension: PhantomData,
316 units: PhantomData,
317 value: rate,
318 }
319 }
320
321 fn bytes_per_sec_f64(self) -> f64 {
322 self.value
323 }
324
325 fn bytes_per_sec_i64(self) -> i64 {
326 round_i64(self.value)
327 }
328
329 fn time_to_transfer(self, size: ByteSize) -> Time {
330 if self.value <= 0.0 {
331 return <Time as TimeExt>::ZERO;
332 }
333 size / self
334 }
335}
336
337pub trait FrequencyExt: Sized + Copy {
339 const ZERO: Self;
341
342 fn from_per_sec(rate: f64) -> Self;
344
345 fn from_per_sec_u64(rate: u64) -> Self;
347
348 fn per_sec_f64(self) -> f64;
350
351 fn per_sec_u64(self) -> u64;
354
355 fn period(self) -> Time;
359}
360
361impl FrequencyExt for Frequency {
362 const ZERO: Self = Self {
363 dimension: PhantomData,
364 units: PhantomData,
365 value: 0.0,
366 };
367
368 fn from_per_sec(rate: f64) -> Self {
369 Self {
370 dimension: PhantomData,
371 units: PhantomData,
372 value: rate,
373 }
374 }
375
376 fn from_per_sec_u64(rate: u64) -> Self {
377 Self::from_per_sec(widen(rate))
378 }
379
380 fn per_sec_f64(self) -> f64 {
381 self.value
382 }
383
384 fn per_sec_u64(self) -> u64 {
385 round_u64(self.value)
386 }
387
388 fn period(self) -> Time {
389 if self.value <= 0.0 {
390 return <Time as TimeExt>::ZERO;
391 }
392 1.0 / self
393 }
394}
395
396pub trait RatioExt: Sized + Copy {
398 const ZERO: Self;
400
401 const ONE: Self;
403
404 fn as_f64(self) -> f64;
406
407 fn percent_f64(self) -> f64;
409}
410
411impl RatioExt for Ratio {
412 const ZERO: Self = Self {
413 dimension: PhantomData,
414 units: PhantomData,
415 value: 0.0,
416 };
417
418 const ONE: Self = Self {
419 dimension: PhantomData,
420 units: PhantomData,
421 value: 1.0,
422 };
423
424 fn as_f64(self) -> f64 {
425 self.value
426 }
427
428 fn percent_f64(self) -> f64 {
429 self.value * 100.0
430 }
431}
432
433pub trait StdDurationExt {
435 fn as_time(&self) -> Time;
437}
438
439impl StdDurationExt for Duration {
440 fn as_time(&self) -> Time {
441 Time::from_std(*self)
442 }
443}
444
445pub mod wire {
453 use super::{ByteRate, ByteRateExt, ByteSize, ByteSizeExt, Time, TimeExt};
454
455 #[must_use]
457 pub fn opt_size_from_bytes_i32(raw: i32) -> Option<ByteSize> {
458 (raw >= 0).then(|| ByteSize::from_bytes_i64(i64::from(raw)))
459 }
460
461 #[must_use]
463 pub fn opt_size_to_bytes_i32(size: Option<ByteSize>) -> i32 {
464 size.map_or(-1, ByteSizeExt::bytes_i32)
465 }
466
467 #[must_use]
469 pub fn opt_size_from_bytes_i64(raw: i64) -> Option<ByteSize> {
470 (raw >= 0).then(|| ByteSize::from_bytes_i64(raw))
471 }
472
473 #[must_use]
475 pub fn opt_size_to_bytes_i64(size: Option<ByteSize>) -> i64 {
476 size.map_or(-1, ByteSizeExt::bytes_i64)
477 }
478
479 #[must_use]
481 pub fn opt_time_from_millis_i64(raw: i64) -> Option<Time> {
482 (raw >= 0).then(|| Time::from_millis(raw))
483 }
484
485 #[must_use]
487 pub fn opt_time_to_millis_i64(time: Option<Time>) -> i64 {
488 time.map_or(-1, TimeExt::millis_i64)
489 }
490
491 #[must_use]
493 pub fn opt_rate_from_bytes_per_sec_i64(raw: i64) -> Option<ByteRate> {
494 (raw >= 0).then(|| ByteRate::from_bytes_per_sec(raw))
495 }
496
497 #[must_use]
499 pub fn opt_rate_to_bytes_per_sec_i64(rate: Option<ByteRate>) -> i64 {
500 rate.map_or(-1, ByteRateExt::bytes_per_sec_i64)
501 }
502}