pub use crate::fraction::Fraction;
use crate::{
duration,
fixed_point::{self, FixedPoint},
time_int::TimeInt,
ConversionError,
};
use core::{convert::TryFrom, mem::size_of, prelude::v1::*};
#[doc(hidden)]
pub use fixed_point::FixedPoint as _;
use num::{CheckedDiv, CheckedMul};
#[doc(inline)]
pub use units::*;
pub trait Rate: Sized + Copy {
fn to_generic<DestInt: TimeInt>(
self,
scaling_factor: Fraction,
) -> Result<Generic<DestInt>, ConversionError>
where
Self: FixedPoint,
DestInt: TryFrom<Self::T>,
{
Ok(Generic::<DestInt>::new(
self.into_ticks(scaling_factor)?,
scaling_factor,
))
}
fn to_duration<Duration: duration::Duration>(&self) -> Result<Duration, ConversionError>
where
Duration: FixedPoint,
Self: FixedPoint,
Duration::T: TryFrom<Self::T>,
{
let conversion_factor = Self::SCALING_FACTOR
.checked_mul(&Duration::SCALING_FACTOR)
.ok_or(ConversionError::Unspecified)?
.recip();
if size_of::<Self::T>() >= size_of::<Duration::T>() {
fixed_point::FixedPoint::from_ticks(
Self::T::from(*conversion_factor.numerator())
.checked_div(
&self
.integer()
.checked_mul(&Self::T::from(*conversion_factor.denominator()))
.ok_or(ConversionError::Overflow)?,
)
.ok_or(ConversionError::DivByZero)?,
Duration::SCALING_FACTOR,
)
} else {
fixed_point::FixedPoint::from_ticks(
Duration::T::from(*conversion_factor.numerator())
.checked_div(
&Duration::T::try_from(self.integer())
.map_err(|_| ConversionError::Overflow)?
.checked_mul(&Duration::T::from(*conversion_factor.denominator()))
.ok_or(ConversionError::Overflow)?,
)
.ok_or(ConversionError::DivByZero)?,
Duration::SCALING_FACTOR,
)
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct Generic<T> {
integer: T,
scaling_factor: Fraction,
}
impl<T: TimeInt> Generic<T> {
pub fn new(integer: T, scaling_factor: Fraction) -> Self {
Self {
integer,
scaling_factor,
}
}
pub fn integer(&self) -> T {
self.integer
}
pub fn scaling_factor(&self) -> &Fraction {
&self.scaling_factor
}
}
impl<T: TimeInt> Rate for Generic<T> {}
#[doc(hidden)]
pub mod units {
use super::*;
use crate::{
fixed_point::{self, FixedPoint},
fraction::Fraction,
time_int::TimeInt,
ConversionError,
};
use core::{
cmp,
convert::TryFrom,
fmt::{self, Formatter},
ops,
};
#[doc(hidden)]
pub use Extensions as _;
macro_rules! impl_rate {
( $name:ident, ($numer:expr, $denom:expr), $desc:literal ) => {
#[doc = $desc]
#[derive(Copy, Clone, Eq, Ord, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct $name<T: TimeInt = u32>(pub T);
impl<T: TimeInt> $name<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T: TimeInt> Rate for $name<T> {}
impl<T: TimeInt> FixedPoint for $name<T> {
type T = T;
const SCALING_FACTOR: Fraction = Fraction::new($numer, $denom);
fn new(value: Self::T) -> Self {
Self(value)
}
fn integer(&self) -> Self::T {
self.0
}
}
impl<T: TimeInt> fmt::Display for $name<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl<T: TimeInt, Rhs: Rate> ops::Add<Rhs> for $name<T>
where
Rhs: FixedPoint,
Self: TryFrom<Rhs>,
{
type Output = Self;
fn add(self, rhs: Rhs) -> Self::Output {
<Self as FixedPoint>::add(self, rhs)
}
}
impl<T: TimeInt, Rhs: Rate> ops::Sub<Rhs> for $name<T>
where
Self: TryFrom<Rhs>,
Rhs: FixedPoint,
{
type Output = Self;
fn sub(self, rhs: Rhs) -> Self::Output {
<Self as FixedPoint>::sub(self, rhs)
}
}
impl<T: TimeInt> ops::Mul<T> for $name<T> {
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
<Self as FixedPoint>::mul(self, rhs)
}
}
impl<T: TimeInt> ops::Div<T> for $name<T> {
type Output = Self;
fn div(self, rhs: T) -> Self::Output {
<Self as FixedPoint>::div(self, rhs)
}
}
impl<T: TimeInt, Rhs: Rate> ops::Rem<Rhs> for $name<T>
where
Self: TryFrom<Rhs>,
Rhs: FixedPoint,
{
type Output = Self;
fn rem(self, rhs: Rhs) -> Self::Output {
<Self as FixedPoint>::rem(self, rhs)
}
}
impl<SourceInt: TimeInt, DestInt: TimeInt> TryFrom<Generic<SourceInt>>
for $name<DestInt>
where
DestInt: TryFrom<SourceInt>,
{
type Error = ConversionError;
fn try_from(generic_rate: Generic<SourceInt>) -> Result<Self, Self::Error> {
fixed_point::FixedPoint::from_ticks(
generic_rate.integer,
generic_rate.scaling_factor,
)
}
}
impl<T: TimeInt> From<$name<T>> for Generic<T> {
fn from(rate: $name<T>) -> Self {
Self::new(rate.integer(), $name::<T>::SCALING_FACTOR)
}
}
};
}
impl_rate![Mebihertz, (1_048_576, 1), "Hertz × 1,048,576"];
impl_rate![Megahertz, (1_000_000, 1), "Hertz × 1,000,000"];
impl_rate![Kibihertz, (1_024, 1), "Hertz × 1,024"];
impl_rate![Kilohertz, (1_000, 1), "Hertz × 1,000"];
impl_rate![Hertz, (1, 1), "Hertz"];
impl_rate![Decihertz, (1, 10), "Hertz / 10"];
impl_rate![Centihertz, (1, 100), "Hertz / 100"];
impl_rate![Millihertz, (1, 1_000), "Hertz / 1000"];
impl_rate![Microhertz, (1, 1_000_000), "Hertz / 1,000,000"];
impl_rate![
MebibytesPerSecond,
(1_048_576 * 8, 1),
"Bytes/s × 1,048,576"
];
impl_rate![
MegabytesPerSecond,
(1_000_000 * 8, 1),
"Bytes/s × 1,000,000"
];
impl_rate![KibibytesPerSecond, (1_024 * 8, 1), "Bytes/s × 1,024"];
impl_rate![KilobytesPerSecond, (1_000 * 8, 1), "Bytes/s × 1,000"];
impl_rate![BytesPerSecond, (8, 1), "Bytes/s"];
impl_rate![MebibitsPerSecond, (1_048_576, 1), "Bits/s × 1,048,576"];
impl_rate![MegabitsPerSecond, (1_000_000, 1), "Bits/s × 1,000,000"];
impl_rate![KibibitsPerSecond, (1_024, 1), "Bits/s × 1,024"];
impl_rate![KilobitsPerSecond, (1_000, 1), "Bits/s × 1,000"];
impl_rate![BitsPerSecond, (1, 1), "Bits/s"];
impl_rate![Mebibaud, (1_048_576, 1), "Baud × 1,048,576"];
impl_rate![Megabaud, (1_000_000, 1), "Baud × 1,000,000"];
impl_rate![Kibibaud, (1_024, 1), "Baud × 1,024"];
impl_rate![Kilobaud, (1_000, 1), "Baud × 1,000"];
impl_rate![Baud, (1, 1), "Baud"];
macro_rules! impl_conversion {
($name:ident) => {
impl From<$name<u32>> for $name<u64> {
fn from(source: $name<u32>) -> Self {
Self::new(u64::from(source.integer()))
}
}
impl TryFrom<$name<u64>> for $name<u32> {
type Error = ConversionError;
fn try_from(source: $name<u64>) -> Result<Self, Self::Error> {
fixed_point::FixedPoint::from_ticks(
source.integer(),
$name::<u64>::SCALING_FACTOR,
)
}
}
impl<T: TimeInt, RhsInt: TimeInt> cmp::PartialEq<$name<RhsInt>> for $name<T>
where
T: TryFrom<RhsInt>,
{
fn eq(&self, rhs: &$name<RhsInt>) -> bool {
match T::try_from(rhs.integer()) {
Ok(rhs_value) => self.integer() == rhs_value,
Err(_) => false
}
}
}
impl<T: TimeInt, RhsInt: TimeInt> PartialOrd<$name<RhsInt>> for $name<T>
where
T: TryFrom<RhsInt>,
{
fn partial_cmp(&self, rhs: &$name<RhsInt>) -> Option<core::cmp::Ordering> {
match T::try_from(rhs.integer()) {
Ok(rhs_integer) => Some(self.integer().cmp(&rhs_integer)),
Err(_) => Some(core::cmp::Ordering::Less),
}
}
}
};
(once, $big:ident, $small:ident) => {
impl<T: TimeInt> From<$small<T>> for $big<T>
{
fn from(small: $small<T>) -> Self {
if let Ok(v) = fixed_point::FixedPoint::from_ticks(small.integer(), $small::<T>::SCALING_FACTOR) {
v
} else {
panic!("From failed")
}
}
}
impl From<$small<u32>> for $big<u64>
{
fn from(small: $small<u32>) -> Self {
if let Ok(v) = fixed_point::FixedPoint::from_ticks(small.integer(), $small::<u32>::SCALING_FACTOR) {
v
} else {
panic!("From failed")
}
}
}
impl TryFrom<$small<u64>> for $big<u32>
{
type Error = ConversionError;
fn try_from(small: $small<u64>) -> Result<Self, Self::Error> {
fixed_point::FixedPoint::from_ticks(
small.integer(),
$small::<u64>::SCALING_FACTOR,
)
}
}
impl From<$big<u32>> for $small<u64>
{
fn from(big: $big<u32>) -> Self {
if let Ok(v) = fixed_point::FixedPoint::from_ticks(big.integer(), $big::<u32>::SCALING_FACTOR) {
v
} else {
panic!("From failed")
}
}
}
impl<T: TimeInt> TryFrom<$big<T>> for $small<T>
{
type Error = ConversionError;
fn try_from(big: $big<T>) -> Result<Self, Self::Error> {
fixed_point::FixedPoint::from_ticks(
big.integer(),
$big::<T>::SCALING_FACTOR,
)
}
}
impl TryFrom<$big<u64>> for $small<u32>
{
type Error = ConversionError;
fn try_from(big: $big<u64>) -> Result<Self, Self::Error> {
fixed_point::FixedPoint::from_ticks(
big.integer(),
$big::<u64>::SCALING_FACTOR,
)
}
}
impl<T: TimeInt, RhsInt: TimeInt> cmp::PartialEq<$small<RhsInt>> for $big<T>
where
$small<RhsInt>: PartialEq<$big<T>>,
{
fn eq(&self, rhs: &$small<RhsInt>) -> bool {
<$small::<RhsInt> as PartialEq<$big<T>>>::eq(rhs, self)
}
}
impl<T: TimeInt, RhsInt: TimeInt> cmp::PartialEq<$big<RhsInt>> for $small<T>
where
Self: TryFrom<$big<RhsInt>>,
{
fn eq(&self, rhs: &$big<RhsInt>) -> bool {
match Self::try_from(*rhs) {
Ok(rhs) => *self == rhs,
Err(_) => false
}
}
}
impl<T: TimeInt, RhsInt: TimeInt> PartialOrd<$small<RhsInt>> for $big<T>
where
$small<RhsInt>: TryFrom<Self> + Ord,
{
fn partial_cmp(&self, rhs: &$small<RhsInt>) -> Option<core::cmp::Ordering> {
match $small::<RhsInt>::try_from(*self) {
Ok(lhs) => Some(lhs.cmp(&rhs)),
Err(_) => Some(core::cmp::Ordering::Greater),
}
}
}
impl<T: TimeInt, RhsInt: TimeInt> PartialOrd<$big<RhsInt>> for $small<T>
where
Self: TryFrom<$big<RhsInt>>,
{
fn partial_cmp(&self, rhs: &$big<RhsInt>) -> Option<core::cmp::Ordering> {
match Self::try_from(*rhs) {
Ok(rhs) => Some((*self).cmp(&rhs)),
Err(_) => Some(core::cmp::Ordering::Less),
}
}
}
};
($big:ident; $($small:ident),+) => {
impl_conversion![$big];
$(
impl_conversion![once, $big, $small];
)+
};
}
impl_conversion![Mebihertz; Kibihertz, Hertz];
impl_conversion![Kibihertz; Hertz];
impl_conversion![Megahertz; Kilohertz, Hertz];
impl_conversion![Kilohertz; Hertz];
impl_conversion![Hertz];
impl_conversion![Decihertz; Hertz];
impl_conversion![Centihertz; Hertz];
impl_conversion![Millihertz; Hertz];
impl_conversion![Microhertz; Hertz];
impl_conversion![MebibytesPerSecond; MebibitsPerSecond, KibibytesPerSecond, KibibitsPerSecond, BytesPerSecond, BitsPerSecond];
impl_conversion![MebibitsPerSecond; KibibytesPerSecond, KibibitsPerSecond, BytesPerSecond, BitsPerSecond];
impl_conversion![KibibytesPerSecond; KibibitsPerSecond, BytesPerSecond, BitsPerSecond];
impl_conversion![KibibitsPerSecond; BytesPerSecond, BitsPerSecond];
impl_conversion![MegabytesPerSecond; MegabitsPerSecond, KilobytesPerSecond, KilobitsPerSecond, BytesPerSecond, BitsPerSecond];
impl_conversion![MegabitsPerSecond; KilobytesPerSecond, KilobitsPerSecond, BytesPerSecond, BitsPerSecond ];
impl_conversion![KilobytesPerSecond; KilobitsPerSecond, BytesPerSecond, BitsPerSecond ];
impl_conversion![KilobitsPerSecond; BytesPerSecond, BitsPerSecond];
impl_conversion![BytesPerSecond; BitsPerSecond];
impl_conversion![BitsPerSecond];
impl_conversion![Mebibaud; Kibibaud, Baud];
impl_conversion![Kibibaud; Baud];
impl_conversion![Megabaud; Kilobaud, Baud];
impl_conversion![Kilobaud; Baud];
impl_conversion![Baud];
#[allow(non_snake_case)]
pub trait Extensions: TimeInt {
fn MiHz(self) -> Mebihertz<Self> {
Mebihertz::new(self)
}
fn MHz(self) -> Megahertz<Self> {
Megahertz::new(self)
}
fn KiHz(self) -> Kibihertz<Self> {
Kibihertz::new(self)
}
fn kHz(self) -> Kilohertz<Self> {
Kilohertz::new(self)
}
fn Hz(self) -> Hertz<Self> {
Hertz::new(self)
}
fn MiBps(self) -> MebibytesPerSecond<Self> {
MebibytesPerSecond::new(self)
}
fn MBps(self) -> MegabytesPerSecond<Self> {
MegabytesPerSecond::new(self)
}
fn KiBps(self) -> KibibytesPerSecond<Self> {
KibibytesPerSecond::new(self)
}
fn kBps(self) -> KilobytesPerSecond<Self> {
KilobytesPerSecond::new(self)
}
fn Bps(self) -> BytesPerSecond<Self> {
BytesPerSecond::new(self)
}
fn Mibps(self) -> MebibitsPerSecond<Self> {
MebibitsPerSecond::new(self)
}
fn Mbps(self) -> MegabitsPerSecond<Self> {
MegabitsPerSecond::new(self)
}
fn Kibps(self) -> KibibitsPerSecond<Self> {
KibibitsPerSecond::new(self)
}
fn kbps(self) -> KilobitsPerSecond<Self> {
KilobitsPerSecond::new(self)
}
fn bps(self) -> BitsPerSecond<Self> {
BitsPerSecond::new(self)
}
fn MiBd(self) -> Mebibaud<Self> {
Mebibaud::new(self)
}
fn MBd(self) -> Megabaud<Self> {
Megabaud::new(self)
}
fn KiBd(self) -> Kibibaud<Self> {
Kibibaud::new(self)
}
fn kBd(self) -> Kilobaud<Self> {
Kilobaud::new(self)
}
fn Bd(self) -> Baud<Self> {
Baud::new(self)
}
}
impl Extensions for u32 {}
}
#[cfg(test)]
mod tests {}