ohms/
current.rs

1use crate::assert_positive_float;
2use core::{cmp, fmt, ops};
3
4/// Represents a current value, stored as whole microamps (μA) as a 64-bit value.
5/// This value can only be positive.
6///
7/// **Reminder:** `1000 μA = 1 mA, 1000 mA = 1 A`
8///
9/// This is an immutable type. Any math operators return a new `Current` value.
10///
11/// # Creating a Current value
12/// You can create a `Current` value using the `from_micro_amps` method, or using one of the
13/// extension methods on integer and floating-point types:
14///
15/// ```rust
16/// use ohms::prelude::*;
17///
18/// let c1 = Current::from_micro_amps(1000); // 1mA
19///
20/// // More ergonomic:
21/// let c2 = 100.milli_amps(); // 0.1A
22/// let c3 = 3.2.amps(); // 3.2A
23/// ```
24///
25/// # Comparing Current values
26/// You can compare two `Current` values using the `==`, `!=`, `<`, `>`, `<=` and `>=` operators.
27///
28/// ```rust
29/// use ohms::prelude::*;
30///
31/// let c1 = 220.milli_amps(); // 220mA
32/// let c2 = 1.2.amps(); // 1.2A
33///
34/// if c1 > c2 {
35///     println!("{} is greater than {}", c1, c2);
36/// } else {
37///     println!("{} is less than or equal to {}", c1, c2);
38/// }
39/// ```
40///
41/// # Combining Current values
42/// You can use the `+` and `-` operators to add and subtract `Current` values from each other.
43/// The result is a new `Current` value, rounded down to the nearest whole microamp (μA).
44///
45/// If the result of the operation would overflow or underflow, the operation will panic.
46///
47/// ```rust
48/// use ohms::prelude::*;
49///
50/// let c1 = 500.milli_amps(); // 0.5A
51/// let c2 = 1.1.amps(); // 1.1A
52///
53/// let sum = c1 + c2; // 1.6A
54/// let diff = c2 - 300.milli_amps(); // 0.8A
55/// ```
56///
57/// # Scaling Current values
58/// You can use the `*` and `/` operators to scale `Current` values by an integer or floating-point value.
59/// The result is a new `Current` value, rounded down to the nearest whole microamp (μA).
60///
61/// If the result of operation would overflow or underflow, the operation will panic.
62///
63/// If the result of the operation would be infinite or NaN, the operation will panic.
64///
65/// ```rust
66/// use ohms::prelude::*;
67///
68/// let c1 = 200.milli_amps(); // 200mA
69/// let c2 = c1 * 3; // 600mA
70///
71/// let r3 = 1.5.amps(); // 1.5A
72/// let r4 = r3 / 2.5; // 0.6A
73/// ```
74///
75/// # Converting to other denominations
76/// You can use the `micro_amps`, `milli_amps`, and `amps`, methods to convert a `Current`
77/// value to a numeric value in the specified denomination.
78///
79/// ```rust
80/// use ohms::prelude::*;
81///
82/// let c1 = 1.2.amps(); // 1.2A
83///
84/// println!("{:.3} A is {:.1} mA", c1.amps(), c1.milli_amps());
85/// ```
86///
87#[derive(Clone, Copy, Debug)]
88pub struct Current {
89    raw: u64,
90}
91
92impl Current {
93    /// Creates a new `Current` from a number of whole microamps (μA).
94    ///
95    /// It is recommended to use the `micro_amps`, `milli_amps`, and `amps`, extension
96    /// methods on integer and floating-point types instead.
97    #[inline]
98    pub const fn from_micro_amps(value: u64) -> Self {
99        Self { raw: value }
100    }
101
102    /// Returns the current value in whole microamps (μA).
103    #[inline]
104    pub const fn micro_amps(&self) -> u64 {
105        self.raw
106    }
107
108    /// Returns the current value in fractional milliamps (A).
109    #[inline]
110    pub fn milli_amps(&self) -> f64 {
111        self.raw as f64 / 1_000f64
112    }
113
114    /// Returns the current value in fractional amps (A).
115    #[inline]
116    pub fn amps(&self) -> f64 {
117        self.raw as f64 / 1_000_000f64
118    }
119
120    /// Returns whether the current value is zero amps (0A).
121    #[inline]
122    pub const fn is_zero(&self) -> bool {
123        self.raw == 0
124    }
125
126    /// Returns a `Current` value of zero amps (0A).
127    #[inline]
128    pub const fn zero() -> Self {
129        Self::from_micro_amps(0)
130    }
131}
132
133impl PartialEq for Current {
134    #[inline]
135    fn eq(&self, other: &Self) -> bool {
136        self.raw == other.raw
137    }
138}
139
140impl Eq for Current {}
141
142impl PartialOrd for Current {
143    #[inline]
144    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
145        self.raw.partial_cmp(&other.raw)
146    }
147}
148
149impl Ord for Current {
150    #[inline]
151    fn cmp(&self, other: &Self) -> cmp::Ordering {
152        self.raw.cmp(&other.raw)
153    }
154}
155
156impl ops::Add for Current {
157    type Output = Self;
158
159    /// Adds two `Current` values together, returning a new `Current` value.
160    #[inline]
161    fn add(self, other: Self) -> Self {
162        self.raw
163            .checked_add(other.raw)
164            .map(Self::from_micro_amps)
165            .expect("Overflow when adding current values")
166    }
167}
168
169impl ops::Sub for Current {
170    type Output = Self;
171
172    /// Subtracts one `Current` value from another, returning a new `Current` value.
173    #[inline]
174    fn sub(self, other: Self) -> Self {
175        self.raw
176            .checked_sub(other.raw)
177            .map(Self::from_micro_amps)
178            .expect("Overflow when subtracting current values")
179    }
180}
181
182macro_rules! impl_mul_for_integer {
183    ($i:ty) => {
184        impl ops::Mul<$i> for Current {
185            type Output = Self;
186
187            #[inline]
188            #[allow(unused_comparisons)]
189            fn mul(self, scale_factor: $i) -> Self {
190                if scale_factor < 0 {
191                    panic!("Cannot multiply current value by negative value")
192                }
193                self.raw
194                    .checked_mul(scale_factor as u64)
195                    .map(Self::from_micro_amps)
196                    .expect("Overflow when multiplying current value")
197            }
198        }
199    };
200}
201
202impl_mul_for_integer!(u8);
203impl_mul_for_integer!(u16);
204impl_mul_for_integer!(u32);
205impl_mul_for_integer!(u64);
206impl_mul_for_integer!(i8);
207impl_mul_for_integer!(i16);
208impl_mul_for_integer!(i32);
209impl_mul_for_integer!(i64);
210
211impl ops::Mul<f32> for Current {
212    type Output = Self;
213
214    /// Multiplies a `Current` value by a floating-point value, returning a new `Current` value.
215    #[inline]
216    fn mul(self, scale_factor: f32) -> Self {
217        self * scale_factor as f64
218    }
219}
220
221impl ops::Mul<f64> for Current {
222    type Output = Self;
223
224    /// Multiplies a `Current` value by a floating-point value, returning a new `Current` value.
225    #[inline]
226    fn mul(self, scale_factor: f64) -> Self {
227        let result = match scale_factor {
228            _ if scale_factor.is_infinite() => {
229                panic!("Cannot multiply current value by infinity")
230            }
231            _ if scale_factor.is_nan() => panic!("Cannot multiply current value by NaN"),
232            _ if scale_factor.is_sign_negative() => {
233                panic!("Cannot multiply current value by negative value")
234            }
235            _ => self.raw as f64 * scale_factor,
236        };
237
238        Self::from_micro_amps(result as u64)
239    }
240}
241
242macro_rules! impl_div_for_integer {
243    ($i:ty) => {
244        impl ops::Div<$i> for Current {
245            type Output = Self;
246
247            /// Divides a `Current` value by an integer value, returning a new `Current` value.
248            #[inline]
249            #[allow(unused_comparisons)]
250            fn div(self, divisor: $i) -> Self {
251                if divisor == 0 {
252                    panic!("Cannot divide current value by zero");
253                } else if divisor < 0 {
254                    panic!("Cannot divide current value by negative value");
255                }
256                self.raw
257                    .checked_div(divisor as u64)
258                    .map(Self::from_micro_amps)
259                    .expect("Overflow when dividing current value")
260            }
261        }
262    };
263}
264
265impl_div_for_integer!(u8);
266impl_div_for_integer!(u16);
267impl_div_for_integer!(u32);
268impl_div_for_integer!(u64);
269impl_div_for_integer!(i8);
270impl_div_for_integer!(i16);
271impl_div_for_integer!(i32);
272impl_div_for_integer!(i64);
273
274impl ops::Div<f32> for Current {
275    type Output = Self;
276
277    /// Divides a `Current` value by a floating-point value, returning a new `Current` value.
278    #[inline]
279    fn div(self, divisor: f32) -> Self {
280        self / divisor as f64
281    }
282}
283
284impl ops::Div<f64> for Current {
285    type Output = Self;
286
287    /// Divides a `Current` value by a floating-point value, returning a new `Current` value.
288    #[inline]
289    fn div(self, divisor: f64) -> Self {
290        let result = match divisor {
291            _ if divisor == 0f64 => panic!("Cannot divide current value by zero"),
292            _ if divisor.is_infinite() => {
293                panic!("Cannot divide current value by infinity")
294            }
295            _ if divisor.is_nan() => panic!("Cannot divide current value by NaN"),
296            _ if divisor.is_sign_negative() => {
297                panic!("Cannot divide current value by negative value")
298            }
299            _ => (self.raw as f64) / divisor,
300        };
301
302        Self::from_micro_amps(result as u64)
303    }
304}
305
306/// Extension trait for simple short-hands for creating `Current` values from integer values.
307pub trait FromInteger {
308    /// Creates a new `Current` from a number of whole microamps (μA).
309    fn micro_amps(self) -> Current;
310
311    /// Creates a new `Current` from a number of whole milliamps (mA).
312    fn milli_amps(self) -> Current;
313
314    /// Creates a new `Current` from a number of whole amps (A).
315    fn amps(self) -> Current;
316}
317
318macro_rules! impl_current_from_integer {
319    ($i:ty) => {
320        impl FromInteger for $i {
321            #[inline]
322            fn micro_amps(self) -> Current {
323                Current::from_micro_amps(self as u64)
324            }
325
326            #[inline]
327            fn milli_amps(self) -> Current {
328                let milliamps = (self as u64)
329                    .checked_mul(1_000)
330                    .expect("Overflow when converting milliamps to microamps");
331                Current::from_micro_amps(milliamps)
332            }
333
334            #[inline]
335            fn amps(self) -> Current {
336                let milliamps = (self as u64)
337                    .checked_mul(1_000_000)
338                    .expect("Overflow when converting amps to microamps");
339                Current::from_micro_amps(milliamps)
340            }
341        }
342    };
343}
344
345impl_current_from_integer!(u8);
346impl_current_from_integer!(u16);
347impl_current_from_integer!(u32);
348impl_current_from_integer!(u64);
349impl_current_from_integer!(i8);
350impl_current_from_integer!(i16);
351impl_current_from_integer!(i32);
352impl_current_from_integer!(i64);
353
354/// Extension trait for simple short-hands for creating `Current` values from floating-point values.
355pub trait FromFloat {
356    /// Creates a new `Current` from a number of fractional microamps (μA).
357    ///
358    /// The fractional part is rounded down to the nearest whole microamp (μA).
359    fn micro_amps(self) -> Current;
360
361    /// Creates a new `Current` from a number of fractional milliamps (mA).
362    ///
363    /// The fractional part is rounded down to the nearest whole microamp (μA).
364    fn milli_amps(self) -> Current;
365
366    /// Creates a new `Current` from a number of fractional amps (A).
367    ///
368    /// The fractional part is rounded down to the nearest whole microamp (μA).
369    fn amps(self) -> Current;
370}
371
372macro_rules! impl_current_from_float {
373    ($f:ty) => {
374        impl FromFloat for $f {
375            #[inline]
376            fn micro_amps(self) -> Current {
377                assert_positive_float!(self);
378                Current::from_micro_amps(self as u64)
379            }
380
381            #[inline]
382            fn milli_amps(self) -> Current {
383                assert_positive_float!(self);
384                let milliamps = (self as f64) * 1_000f64;
385                Current::from_micro_amps(milliamps as u64)
386            }
387
388            #[inline]
389            fn amps(self) -> Current {
390                assert_positive_float!(self);
391                let milliamps = (self as f64) * 1_000_000f64;
392                Current::from_micro_amps(milliamps as u64)
393            }
394        }
395    };
396}
397
398impl_current_from_float!(f32);
399impl_current_from_float!(f64);
400
401impl fmt::Display for Current {
402    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
403        let (value, unit) = match self.raw {
404            0..=999 => (self.raw as f64, "μA"),
405            1_000..=999_999 => ((self.raw as f64) / 1_000f64, "mA"),
406            _ => ((self.raw as f64) / 1_000_000f64, "A"),
407        };
408
409        write!(f, "{value:.2} {unit}")
410    }
411}