1use std::{
2 fmt::Display,
3 ops::{Add, Div, Mul, Sub},
4 str::FromStr,
5};
6
7pub use error::*;
8
9mod error;
10mod tests;
11
12#[cfg(feature = "chrono")]
13mod chrono;
14#[cfg(feature = "clap")]
15mod clap;
16#[cfg(feature = "num-traits")]
17mod num_traits;
18#[cfg(feature = "schemars")]
19mod schemars;
20#[cfg(feature = "serde")]
21mod serde;
22#[cfg(feature = "time")]
23mod time;
24
25pub const KILOHERTZ: u64 = 1_000;
27
28pub const MEGAHERTZ: u64 = 1_000_000;
30
31pub const GIGAHERTZ: u64 = 1_000_000_000;
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
68#[repr(transparent)]
69pub struct Frequency(pub u64);
70
71unsafe impl Send for Frequency {}
72unsafe impl Sync for Frequency {}
73
74impl Frequency {
75 pub const ZERO: Self = Self(0);
82
83 pub const HERTZ: Self = Self(1);
90
91 pub const KILOHERTZ: Self = Self(KILOHERTZ);
98
99 pub const MEGAHERTZ: Self = Self(MEGAHERTZ);
106
107 pub const GIGAHERTZ: Self = Self(GIGAHERTZ);
114
115 #[must_use]
116 #[doc(alias = "from_hertz")]
117 pub fn from_hz(hz: u64) -> Self {
118 Self(hz)
119 }
120
121 #[must_use]
122 #[doc(alias = "from_kilohertz")]
123 pub fn from_khz(khz: u64) -> Self {
124 Self(khz * KILOHERTZ)
125 }
126
127 #[must_use]
128 #[doc(alias = "from_megahertz")]
129 pub fn from_mhz(mhz: u64) -> Self {
130 Self(mhz * MEGAHERTZ)
131 }
132
133 #[must_use]
134 #[doc(alias = "from_gigahertz")]
135 pub fn from_ghz(ghz: u64) -> Self {
136 Self(ghz * GIGAHERTZ)
137 }
138
139 #[must_use]
140 #[doc(alias = "as_hertz")]
141 pub fn as_hz(&self) -> u64 {
142 self.0
143 }
144
145 #[must_use]
146 #[doc(alias = "as_kilohertz")]
147 pub fn as_khz(&self) -> u64 {
148 self.as_hz() / KILOHERTZ
149 }
150
151 #[must_use]
152 #[doc(alias = "as_megahertz")]
153 pub fn as_mhz(&self) -> u64 {
154 self.as_hz() / MEGAHERTZ
155 }
156
157 #[must_use]
158 #[doc(alias = "as_gigahertz")]
159 pub fn as_ghz(&self) -> u64 {
160 self.as_hz() / GIGAHERTZ
161 }
162
163 #[must_use]
177 pub fn as_duration(&self) -> std::time::Duration {
178 if self.0 == 0 {
179 std::time::Duration::ZERO
180 } else {
181 std::time::Duration::from_nanos(GIGAHERTZ / self.0)
182 }
183 }
184}
185
186impl Display for Frequency {
187 #[allow(clippy::cast_precision_loss)]
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 let value = self.as_hz();
191
192 if value >= GIGAHERTZ {
193 write!(f, "{:.2} GHz", value as f64 / GIGAHERTZ as f64)
194 } else if value >= MEGAHERTZ {
195 write!(f, "{:.2} MHz", value as f64 / MEGAHERTZ as f64)
196 } else if value >= KILOHERTZ {
197 write!(f, "{:.2} kHz", value as f64 / KILOHERTZ as f64)
198 } else {
199 write!(f, "{value} Hz")
200 }
201 }
202}
203
204impl FromStr for Frequency {
205 type Err = Error;
206
207 fn from_str(s: &str) -> Result<Self> {
208 parse_frequency(s)
209 }
210}
211
212impl TryFrom<&str> for Frequency {
213 type Error = Error;
214
215 fn try_from(s: &str) -> Result<Self> {
216 parse_frequency(s)
217 }
218}
219
220impl TryFrom<String> for Frequency {
221 type Error = Error;
222
223 fn try_from(s: String) -> Result<Self> {
224 parse_frequency(&s)
225 }
226}
227
228impl Add for Frequency {
229 type Output = Self;
230
231 fn add(self, other: Self) -> Self::Output {
232 Self(self.0 + other.0)
233 }
234}
235
236impl Sub for Frequency {
237 type Output = Self;
238
239 fn sub(self, other: Self) -> Self::Output {
240 Self(self.0 - other.0)
241 }
242}
243
244impl Mul<u64> for Frequency {
245 type Output = Self;
246
247 fn mul(self, rhs: u64) -> Self::Output {
248 Self(self.0 * rhs)
249 }
250}
251
252impl Div<u64> for Frequency {
253 type Output = Self;
254
255 fn div(self, rhs: u64) -> Self::Output {
256 Self(self.0 / rhs)
257 }
258}
259
260pub fn parse_frequency(s: &str) -> Result<Frequency> {
285 let s = s.trim().to_lowercase();
286
287 let (value_str, multiplier) = if let Some(value) = s.strip_suffix("ghz") {
288 (value, 1_000_000_000)
289 } else if let Some(value) = s.strip_suffix("mhz") {
290 (value, 1_000_000)
291 } else if let Some(value) = s.strip_suffix("khz") {
292 (value, 1_000)
293 } else if let Some(value) = s.strip_suffix("hz") {
294 (value, 1)
295 } else {
296 return Err(Error::UnknownUnit(s.to_string()));
297 };
298
299 let value = value_str
300 .trim()
301 .parse::<f64>()
302 .map_err(|_| Error::InvalidValue(value_str.to_string()))?;
303
304 if value.is_sign_negative() {
306 return Err(Error::InvalidValue(value_str.to_string()));
307 }
308
309 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
311 let hz = (value * f64::from(multiplier)).round() as u64;
312 Ok(Frequency(hz))
313}