1use crate::error::CoreError;
35use serde::{Deserialize, Serialize};
36use std::{fmt::Display, str::FromStr};
37use uom::{
38 fmt::DisplayStyle,
39 si::{f64::Power as BasePower, power as power_unit},
40};
41
42#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Deserialize, Serialize)]
47pub struct Power(BasePower);
48
49impl Display for Power {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 if f.alternate() {
60 if self.value() < 0.001 {
61 self.0
62 .into_format_args(power_unit::milliwatt, DisplayStyle::Description)
63 .fmt(f)
64 } else if self.value() >= 1000.0 {
65 self.0
66 .into_format_args(power_unit::kilowatt, DisplayStyle::Description)
67 .fmt(f)
68 } else {
69 self.0
70 .into_format_args(power_unit::watt, DisplayStyle::Description)
71 .fmt(f)
72 }
73 } else {
74 self.0
75 .into_format_args(power_unit::watt, DisplayStyle::Abbreviation)
76 .fmt(f)
77 }
78 }
79}
80
81impl FromStr for Power {
82 type Err = CoreError;
83
84 fn from_str(s: &str) -> Result<Self, Self::Err> {
85 BasePower::from_str(s)
86 .map(Self)
87 .map_err(|_| CoreError::InvalidValueFromStr(s.to_string(), "Power"))
88 }
89}
90
91impl From<BasePower> for Power {
92 fn from(value: BasePower) -> Self {
93 Self(value)
94 }
95}
96
97impl From<f64> for Power {
98 fn from(value: f64) -> Self {
99 Self::watts(value)
100 }
101}
102
103impl From<Power> for BasePower {
104 fn from(value: Power) -> Self {
105 value.0
106 }
107}
108
109impl From<Power> for f64 {
110 fn from(value: Power) -> Self {
111 value.0.value
112 }
113}
114
115impl AsRef<BasePower> for Power {
116 fn as_ref(&self) -> &BasePower {
117 &self.0
118 }
119}
120
121impl Power {
122 #[inline(always)]
123 pub fn milliwatts(value: f64) -> Self {
124 Self(BasePower::new::<power_unit::milliwatt>(value))
125 }
126
127 #[inline(always)]
128 pub fn watts(value: f64) -> Self {
129 Self(BasePower::new::<power_unit::watt>(value))
130 }
131
132 #[inline(always)]
133 pub fn kilowatts(value: f64) -> Self {
134 Self(BasePower::new::<power_unit::kilowatt>(value))
135 }
136
137 #[inline(always)]
138 pub fn from_dc_circuit(voltage: f64, current: f64) -> Self {
139 Self::watts(voltage * current)
140 }
141
142 #[inline(always)]
143 pub fn from_ac_circuit(voltage: f64, current: f64, factor: f64) -> Self {
144 assert!((0.0..=1.0).contains(&factor));
145 Self::watts(voltage * current * factor)
146 }
147
148 pub const fn value(&self) -> f64 {
149 self.0.value
150 }
151}
152
153pub fn watts(value: f64) -> Power {
158 Power::watts(value)
159}
160
161pub fn milliwatts(value: f64) -> Power {
162 Power::milliwatts(value)
163}
164
165pub fn kilowatts(value: f64) -> Power {
166 Power::kilowatts(value)
167}
168
169#[cfg(test)]
174mod tests {
175 use super::Power;
176 use pretty_assertions::assert_eq;
177
178 #[test]
179 fn test_display_watts() {
180 assert_eq!("5 W", &Power::watts(5.0).to_string());
181 assert_eq!("100 W", &Power::watts(100.0).to_string());
182 }
183
184 #[test]
185 fn test_alternate_display() {
186 assert_eq!("0.5 milliwatts", &format!("{:#}", Power::milliwatts(0.5)));
188 assert_eq!("5 watts", &format!("{:#}", Power::watts(5.0)));
190 assert_eq!("1.5 kilowatts", &format!("{:#}", Power::kilowatts(1.5)));
192 }
193
194 #[test]
195 fn test_from_dc_circuit() {
196 let p = Power::from_dc_circuit(13.8, 10.0);
197 assert!((p.value() - 138.0).abs() < 1e-9);
198 }
199
200 #[test]
201 fn test_from_ac_circuit() {
202 let p = Power::from_ac_circuit(120.0, 5.0, 0.85);
203 assert!((p.value() - 510.0).abs() < 1e-9);
204 }
205
206 #[test]
207 #[should_panic]
208 fn test_from_ac_circuit_factor_above_one_panics() {
209 Power::from_ac_circuit(120.0, 5.0, 1.5);
210 }
211
212 #[test]
213 fn test_from_f64_is_watts() {
214 let p: Power = 100.0_f64.into();
215 assert_eq!(p.value(), Power::watts(100.0).value());
216 }
217}