use std::{
fmt::Display,
ops::{Add, Div, Mul, Sub},
str::FromStr,
};
pub use error::*;
mod error;
mod tests;
#[cfg(feature = "chrono")]
mod chrono;
#[cfg(feature = "clap")]
mod clap;
#[cfg(feature = "num-traits")]
mod num_traits;
#[cfg(feature = "schemars")]
mod schemars;
#[cfg(feature = "serde")]
mod serde;
#[cfg(feature = "time")]
mod time;
pub const KILOHERTZ: u64 = 1_000;
pub const MEGAHERTZ: u64 = 1_000_000;
pub const GIGAHERTZ: u64 = 1_000_000_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
#[repr(transparent)]
pub struct Frequency(pub u64);
unsafe impl Send for Frequency {}
unsafe impl Sync for Frequency {}
impl Frequency {
pub const ZERO: Self = Self(0);
pub const HERTZ: Self = Self(1);
pub const KILOHERTZ: Self = Self(KILOHERTZ);
pub const MEGAHERTZ: Self = Self(MEGAHERTZ);
pub const GIGAHERTZ: Self = Self(GIGAHERTZ);
#[must_use]
#[doc(alias = "from_hertz")]
pub fn from_hz(hz: u64) -> Self {
Self(hz)
}
#[must_use]
#[doc(alias = "from_kilohertz")]
pub fn from_khz(khz: u64) -> Self {
Self(khz * KILOHERTZ)
}
#[must_use]
#[doc(alias = "from_megahertz")]
pub fn from_mhz(mhz: u64) -> Self {
Self(mhz * MEGAHERTZ)
}
#[must_use]
#[doc(alias = "from_gigahertz")]
pub fn from_ghz(ghz: u64) -> Self {
Self(ghz * GIGAHERTZ)
}
#[must_use]
#[doc(alias = "as_hertz")]
pub fn as_hz(&self) -> u64 {
self.0
}
#[must_use]
#[doc(alias = "as_kilohertz")]
pub fn as_khz(&self) -> u64 {
self.as_hz() / KILOHERTZ
}
#[must_use]
#[doc(alias = "as_megahertz")]
pub fn as_mhz(&self) -> u64 {
self.as_hz() / MEGAHERTZ
}
#[must_use]
#[doc(alias = "as_gigahertz")]
pub fn as_ghz(&self) -> u64 {
self.as_hz() / GIGAHERTZ
}
#[must_use]
pub fn as_duration(&self) -> std::time::Duration {
if self.0 == 0 {
std::time::Duration::ZERO
} else {
std::time::Duration::from_nanos(GIGAHERTZ / self.0)
}
}
}
impl Display for Frequency {
#[allow(clippy::cast_precision_loss)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let value = self.as_hz();
if value >= GIGAHERTZ {
write!(f, "{:.2} GHz", value as f64 / GIGAHERTZ as f64)
} else if value >= MEGAHERTZ {
write!(f, "{:.2} MHz", value as f64 / MEGAHERTZ as f64)
} else if value >= KILOHERTZ {
write!(f, "{:.2} kHz", value as f64 / KILOHERTZ as f64)
} else {
write!(f, "{value} Hz")
}
}
}
impl FromStr for Frequency {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
parse_frequency(s)
}
}
impl TryFrom<&str> for Frequency {
type Error = Error;
fn try_from(s: &str) -> Result<Self> {
parse_frequency(s)
}
}
impl TryFrom<String> for Frequency {
type Error = Error;
fn try_from(s: String) -> Result<Self> {
parse_frequency(&s)
}
}
impl Add for Frequency {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
Self(self.0 + other.0)
}
}
impl Sub for Frequency {
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
Self(self.0 - other.0)
}
}
impl Mul<u64> for Frequency {
type Output = Self;
fn mul(self, rhs: u64) -> Self::Output {
Self(self.0 * rhs)
}
}
impl Div<u64> for Frequency {
type Output = Self;
fn div(self, rhs: u64) -> Self::Output {
Self(self.0 / rhs)
}
}
pub fn parse_frequency(s: &str) -> Result<Frequency> {
let s = s.trim().to_lowercase();
let (value_str, multiplier) = if let Some(value) = s.strip_suffix("ghz") {
(value, 1_000_000_000)
} else if let Some(value) = s.strip_suffix("mhz") {
(value, 1_000_000)
} else if let Some(value) = s.strip_suffix("khz") {
(value, 1_000)
} else if let Some(value) = s.strip_suffix("hz") {
(value, 1)
} else {
return Err(Error::UnknownUnit(s.to_string()));
};
let value = value_str
.trim()
.parse::<f64>()
.map_err(|_| Error::InvalidValue(value_str.to_string()))?;
if value.is_sign_negative() {
return Err(Error::InvalidValue(value_str.to_string()));
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let hz = (value * f64::from(multiplier)).round() as u64;
Ok(Frequency(hz))
}