use crate::compat::Duration;
#[cfg(feature = "alloc")]
use crate::compat::Box;
#[cfg(feature = "std")]
use std::time::Instant;
pub(crate) type ElapsedClockFn = fn() -> Duration;
enum ClockSource {
FnPtr(ElapsedClockFn),
#[cfg(feature = "alloc")]
Boxed(Box<dyn Fn() -> Duration>),
}
impl ClockSource {
fn call(&self) -> Duration {
match self {
ClockSource::FnPtr(f) => f(),
#[cfg(feature = "alloc")]
ClockSource::Boxed(f) => f(),
}
}
}
struct ClockStart {
source: ClockSource,
origin: Duration,
}
pub(crate) struct ElapsedTracker {
start_clock: Option<ClockStart>,
#[cfg(feature = "std")]
start: Option<Instant>,
}
impl ElapsedTracker {
pub(crate) fn new(clock: Option<ElapsedClockFn>) -> Self {
let start_clock = clock.map(|clock| ClockStart {
origin: clock(),
source: ClockSource::FnPtr(clock),
});
Self {
#[cfg(feature = "std")]
start: std_instant_fallback(start_clock.is_some()),
start_clock,
}
}
#[cfg(feature = "alloc")]
pub(crate) fn new_boxed(clock: Box<dyn Fn() -> Duration>) -> Self {
let origin = clock();
Self {
start_clock: Some(ClockStart {
source: ClockSource::Boxed(clock),
origin,
}),
#[cfg(feature = "std")]
start: None,
}
}
pub(crate) fn elapsed(&self) -> Option<Duration> {
self.start_clock
.as_ref()
.map(|start_clock| start_clock.source.call().saturating_sub(start_clock.origin))
.or({
#[cfg(feature = "std")]
{
self.start.map(|start| start.elapsed())
}
#[cfg(not(feature = "std"))]
{
None
}
})
}
}
#[cfg(feature = "std")]
fn std_instant_fallback(has_custom_clock: bool) -> Option<Instant> {
if has_custom_clock {
None
} else {
Some(Instant::now())
}
}