use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
const MILLIS_PER_BINARY_QUARTER_SEC: u128 = 256;
const MILLIS_PER_BINARY_MINUTE: u128 = 65536;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Ord, PartialOrd, FromPrimitive)]
#[repr(u8)]
pub enum Units {
Inactive = 0x00,
MsTime = 0x01,
QsTime = 0x02,
MinuteTime = 0x03,
}
impl From<Units> for u8 {
fn from(units: Units) -> Self {
units as Self
}
}
impl TryFrom<u8> for Units {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Self::from_u8(value).ok_or(value)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct Duration {
time: u16,
units: Units,
}
impl Duration {
pub const MAX_TIME: u16 = 32767;
pub const fn try_new(time: u16, units: Units) -> Result<Self, u16> {
if time > Self::MAX_TIME {
Err(time)
} else {
Ok(Self { time, units })
}
}
#[must_use]
pub const fn time(&self) -> u16 {
self.time
}
#[must_use]
pub const fn units(&self) -> Units {
self.units
}
}
impl TryFrom<Option<std::time::Duration>> for Duration {
type Error = std::time::Duration;
#[expect(clippy::cast_possible_truncation)]
fn try_from(duration: Option<std::time::Duration>) -> Result<Self, Self::Error> {
let Some(duration) = duration else {
return Ok(Self {
time: 0,
units: Units::Inactive,
});
};
let millis = duration.as_millis();
if millis < u128::from(Self::MAX_TIME) {
return Ok(Self {
time: millis as u16,
units: Units::MsTime,
});
}
let binary_quarter_secs = millis / MILLIS_PER_BINARY_QUARTER_SEC;
if binary_quarter_secs < u128::from(Self::MAX_TIME) {
return Ok(Self {
time: binary_quarter_secs as u16,
units: Units::QsTime,
});
}
let binary_minutes = millis / MILLIS_PER_BINARY_MINUTE;
if binary_minutes < u128::from(Self::MAX_TIME) {
return Ok(Self {
time: binary_minutes as u16,
units: Units::MinuteTime,
});
}
Err(duration)
}
}