use crate::duration::Duration;
use crate::helpers::Helpers;
use core::cmp::Ordering;
use core::ops;
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(
feature = "postcard_max_size",
derive(postcard::experimental::max_size::MaxSize)
)]
#[derive(Clone, Copy, Debug)]
pub struct Instant<T, const NOM: u64, const DENOM: u64> {
ticks: T,
}
macro_rules! impl_instant_for_integer {
($i:ty) => {
impl<const NOM: u64, const DENOM: u64> Instant<$i, NOM, DENOM> {
#[doc = concat!("let _i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[inline]
pub const fn from_ticks(ticks: $i) -> Self {
const { assert!(NOM > 0) };
const { assert!(DENOM > 0) };
Instant { ticks }
}
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(234);")]
#[inline]
pub const fn as_ticks(&self) -> $i {
self.ticks
}
#[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
#[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i),"::MAX);")]
#[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[inline]
pub const fn const_cmp(self, other: Self) -> Ordering {
if self.ticks == other.ticks {
Ordering::Equal
} else {
let v = self.ticks.wrapping_sub(other.ticks);
if v > <$i>::MAX / 2 {
Ordering::Less
} else if v < <$i>::MAX / 2 {
Ordering::Greater
} else {
Ordering::Equal
}
}
}
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(11);")]
#[inline]
pub const fn duration_since_epoch(self) -> Duration<$i, NOM, DENOM> {
Duration::<$i, NOM, DENOM>::from_ticks(self.as_ticks())
}
#[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
#[inline]
pub const fn checked_duration_since(
self,
other: Self,
) -> Option<Duration<$i, NOM, DENOM>> {
match self.const_cmp(other) {
Ordering::Greater | Ordering::Equal => {
Some(Duration::<$i, NOM, DENOM>::from_ticks(
self.ticks.wrapping_sub(other.ticks),
))
}
Ordering::Less => None,
}
}
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
pub const fn checked_sub_duration<const O_NOM: u64, const O_DENOM: u64>(
self,
other: Duration<$i, O_NOM, O_DENOM>,
) -> Option<Self> {
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
Some(Self::from_ticks(
self.ticks.wrapping_sub(other.as_ticks()),
))
} else {
if let Some(lh) = other
.as_ticks()
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
{
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
Some(Self::from_ticks(self.ticks.wrapping_sub(ticks)))
} else {
None
}
}
}
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
pub const fn checked_add_duration<const O_NOM: u64, const O_DENOM: u64>(
self,
other: Duration<$i, O_NOM, O_DENOM>,
) -> Option<Self> {
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
Some(Self::from_ticks(
self.ticks.wrapping_add(other.as_ticks()),
))
} else {
if let Some(lh) = other
.as_ticks()
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
{
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
Some(Self::from_ticks(self.ticks.wrapping_add(ticks)))
} else {
None
}
}
}
}
#[allow(clippy::non_canonical_partial_ord_impl)]
impl<const NOM: u64, const DENOM: u64> PartialOrd for Instant<$i, NOM, DENOM> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.const_cmp(*other))
}
}
impl<const NOM: u64, const DENOM: u64> Ord for Instant<$i, NOM, DENOM> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.const_cmp(*other)
}
}
impl<const NOM: u64, const DENOM: u64> PartialEq for Instant<$i, NOM, DENOM> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.ticks.eq(&other.ticks)
}
}
impl<const NOM: u64, const DENOM: u64> Eq for Instant<$i, NOM, DENOM> {}
impl<const NOM: u64, const DENOM: u64> ops::Sub<Instant<$i, NOM, DENOM>>
for Instant<$i, NOM, DENOM>
{
type Output = Duration<$i, NOM, DENOM>;
#[inline]
#[track_caller]
fn sub(self, other: Self) -> Self::Output {
if let Some(v) = self.checked_duration_since(other) {
v
} else {
panic!("Sub failed! Other > self");
}
}
}
impl<const NOM: u64, const DENOM: u64> ops::Sub<Duration<$i, NOM, DENOM>>
for Instant<$i, NOM, DENOM>
{
type Output = Self;
#[inline]
#[track_caller]
fn sub(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
if let Some(v) = self.checked_sub_duration(other) {
v
} else {
panic!("Sub failed! Overflow");
}
}
}
impl<const NOM: u64, const DENOM: u64> ops::SubAssign<Duration<$i, NOM, DENOM>>
for Instant<$i, NOM, DENOM>
{
#[inline]
#[track_caller]
fn sub_assign(&mut self, other: Duration<$i, NOM, DENOM>) {
*self = *self - other;
}
}
impl<const NOM: u64, const DENOM: u64> ops::Add<Duration<$i, NOM, DENOM>>
for Instant<$i, NOM, DENOM>
{
type Output = Self;
#[inline]
#[track_caller]
fn add(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
if let Some(v) = self.checked_add_duration(other) {
v
} else {
panic!("Add failed! Overflow");
}
}
}
impl<const NOM: u64, const DENOM: u64> ops::AddAssign<Duration<$i, NOM, DENOM>>
for Instant<$i, NOM, DENOM>
{
#[inline]
#[track_caller]
fn add_assign(&mut self, other: Duration<$i, NOM, DENOM>) {
*self = *self + other;
}
}
#[cfg(feature = "defmt")]
impl<const NOM: u64, const DENOM: u64> defmt::Format for Instant<$i, NOM, DENOM> {
fn format(&self, f: defmt::Formatter) {
if NOM == 3_600 && DENOM == 1 {
defmt::write!(f, "{} h", self.ticks)
} else if NOM == 60 && DENOM == 1 {
defmt::write!(f, "{} min", self.ticks)
} else if NOM == 1 && DENOM == 1 {
defmt::write!(f, "{} s", self.ticks)
} else if NOM == 1 && DENOM == 1_000 {
defmt::write!(f, "{} ms", self.ticks)
} else if NOM == 1 && DENOM == 1_000_000 {
defmt::write!(f, "{} us", self.ticks)
} else if NOM == 1 && DENOM == 1_000_000_000 {
defmt::write!(f, "{} ns", self.ticks)
} else {
defmt::write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
}
}
}
impl<const NOM: u64, const DENOM: u64> core::fmt::Display for Instant<$i, NOM, DENOM> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if NOM == 3_600 && DENOM == 1 {
write!(f, "{} h", self.ticks)
} else if NOM == 60 && DENOM == 1 {
write!(f, "{} min", self.ticks)
} else if NOM == 1 && DENOM == 1 {
write!(f, "{} s", self.ticks)
} else if NOM == 1 && DENOM == 1_000 {
write!(f, "{} ms", self.ticks)
} else if NOM == 1 && DENOM == 1_000_000 {
write!(f, "{} us", self.ticks)
} else if NOM == 1 && DENOM == 1_000_000_000 {
write!(f, "{} ns", self.ticks)
} else {
write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
}
}
}
};
}
impl_instant_for_integer!(u32);
impl_instant_for_integer!(u64);
impl<const NOM: u64, const DENOM: u64> ops::Sub<Duration<u32, NOM, DENOM>>
for Instant<u64, NOM, DENOM>
{
type Output = Self;
#[inline]
#[track_caller]
fn sub(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
if let Some(v) = self.checked_sub_duration(other.into()) {
v
} else {
panic!("Sub failed! Overflow");
}
}
}
impl<const NOM: u64, const DENOM: u64> ops::SubAssign<Duration<u32, NOM, DENOM>>
for Instant<u64, NOM, DENOM>
{
#[inline]
#[track_caller]
fn sub_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
*self = *self - other;
}
}
impl<const NOM: u64, const DENOM: u64> ops::Add<Duration<u32, NOM, DENOM>>
for Instant<u64, NOM, DENOM>
{
type Output = Self;
#[inline]
#[track_caller]
fn add(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
if let Some(v) = self.checked_add_duration(other.into()) {
v
} else {
panic!("Add failed! Overflow");
}
}
}
impl<const NOM: u64, const DENOM: u64> ops::AddAssign<Duration<u32, NOM, DENOM>>
for Instant<u64, NOM, DENOM>
{
#[inline]
#[track_caller]
fn add_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
*self = *self + other;
}
}