use std::{borrow, cell, cmp, marker, mem, ops, time};
use o3::base::marker::ThreadBound;
use crate::driver;
mod range;
pub mod timer;
#[doc(hidden)]
pub mod watchdog;
pub use range::Range;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(C)]
struct Point<'d> {
at: time::Instant,
driver: (driver::Invariant<'d>, ThreadBound),
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Clock<'turn, 'd> {
source: &'turn cell::Cell<time::Instant>,
driver: (driver::Invariant<'d>, ThreadBound),
}
#[derive(Clone, Copy)]
pub struct Scope<'d> {
proof: (driver::Invariant<'d>, ThreadBound),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct Origin(time::Instant);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Moment<'d>(Point<'d>);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Deadline<'d>(Point<'d>);
#[repr(transparent)]
pub struct Bound<'d, T> {
inner: T,
driver: (driver::Invariant<'d>, ThreadBound),
}
#[repr(C)]
pub struct At<'d, A> {
access: A,
now: Moment<'d>,
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Sample(time::Instant);
impl Sample {
pub const fn new(now: time::Instant) -> Self {
Self(now)
}
pub(in crate::driver) const fn into_raw(self) -> time::Instant {
self.0
}
}
impl Scope<'_> {
pub fn run<R>(run: impl for<'d> FnOnce(Scope<'d>) -> R) -> R {
run(Scope::new())
}
}
impl<'d> Scope<'d> {
const fn new() -> Self {
Self {
proof: (driver::Invariant(marker::PhantomData), ThreadBound::NEW),
}
}
pub fn now(self) -> Moment<'d> {
Moment::new(time::Instant::now(), self.proof)
}
}
impl Origin {
pub fn elapsed(self) -> time::Duration {
self.0.elapsed()
}
}
impl<'d> Point<'d> {
const fn new(at: time::Instant, driver: (driver::Invariant<'d>, ThreadBound)) -> Self {
Self { at, driver }
}
const fn at(self, at: time::Instant) -> Self {
Self::new(at, self.driver)
}
const fn get(self) -> time::Instant {
self.at
}
fn try_add<D: Copy + borrow::Borrow<time::Duration>>(
self,
duration: D,
) -> Result<Self, Range<D>> {
match self.at.checked_add(*duration.borrow()) {
Some(at) => Ok(self.at(at)),
None => Err(Range::new(Origin(self.at), duration)),
}
}
fn clamp_add<D: Copy + borrow::Borrow<time::Duration>>(self, duration: D) -> Self {
match self.try_add(duration) {
Ok(point) => point,
Err(range) => self.at(range.furthest()),
}
}
}
impl<'turn, 'd> Clock<'turn, 'd> {
pub(in crate::driver) const fn new(
source: &'turn cell::Cell<time::Instant>,
driver: driver::Invariant<'d>,
) -> Self {
Self {
source,
driver: (driver, ThreadBound::NEW),
}
}
pub fn now(self) -> Moment<'d> {
Moment::new(self.source.get(), self.driver)
}
pub fn attach<T>(self, build: impl FnOnce(time::Instant) -> T) -> Bound<'d, T> {
Bound::new(build(self.source.get()), self.driver)
}
pub fn try_attach<T, E>(
self,
build: impl FnOnce(time::Instant) -> Result<T, E>,
) -> Result<Bound<'d, T>, E> {
build(self.source.get()).map(|inner| Bound::new(inner, self.driver))
}
pub fn deadline_at(self, at: time::Instant) -> Deadline<'d> {
Deadline(Point::new(at, self.driver))
}
pub fn at<A>(self, access: A) -> At<'d, A> {
At {
access,
now: self.now(),
}
}
}
impl<'d, T> Bound<'d, T> {
const fn new(inner: T, driver: (driver::Invariant<'d>, ThreadBound)) -> Self {
Self { inner, driver }
}
pub fn map<U>(self, map: impl FnOnce(T) -> U) -> Bound<'d, U> {
Bound::new(map(self.inner), self.driver)
}
}
impl<T> ops::Deref for Bound<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> ops::DerefMut for Bound<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<'d, A> At<'d, A> {
pub fn apply<R>(self, apply: impl FnOnce(A, time::Instant) -> R) -> R {
apply(self.access, self.now.0.get())
}
pub fn deadline(
self,
project: impl FnOnce(A, time::Instant) -> Option<time::Instant>,
) -> Option<Deadline<'d>> {
project(self.access, self.now.0.get()).map(|at| Deadline(self.now.0.at(at)))
}
}
impl<'d, T: ?Sized> At<'d, &mut T> {
pub fn apply_mut<R>(&mut self, apply: impl FnOnce(&mut T, Moment<'d>) -> R) -> R {
apply(&mut *self.access, self.now)
}
}
impl<'d> Moment<'d> {
const fn new(at: time::Instant, driver: (driver::Invariant<'d>, ThreadBound)) -> Self {
Self(Point::new(at, driver))
}
pub const fn retain(self) -> Origin {
Origin(self.0.get())
}
pub fn try_add<D: Copy + borrow::Borrow<time::Duration>>(
self,
duration: D,
) -> Result<Deadline<'d>, Range<D>> {
Ok(Deadline(self.0.try_add(duration)?))
}
pub fn clamp_add<D: Copy + borrow::Borrow<time::Duration>>(self, duration: D) -> Deadline<'d> {
Deadline(self.0.clamp_add(duration))
}
pub fn at<A>(self, access: A) -> At<'d, A> {
At { access, now: self }
}
pub fn moment_at(self, at: time::Instant) -> Self {
Self(self.0.at(at))
}
pub fn deadline_at(self, at: time::Instant) -> Deadline<'d> {
Deadline(self.0.at(at))
}
pub fn since(self, earlier: Self) -> time::Duration {
self.0.get().duration_since(earlier.0.get())
}
pub fn clamp_since(self, earlier: Self) -> time::Duration {
self.0.get().saturating_duration_since(earlier.0.get())
}
pub fn try_since(self, earlier: Self) -> Option<time::Duration> {
self.0.get().checked_duration_since(earlier.0.get())
}
pub fn past_due(self, earlier: Deadline<'d>) -> time::Duration {
self.0.get().duration_since(earlier.0.get())
}
pub const fn into_raw(self) -> time::Instant {
self.0.get()
}
}
impl<'d> Deadline<'d> {
pub fn try_add<D: Copy + borrow::Borrow<time::Duration>>(
self,
duration: D,
) -> Result<Self, Range<D>> {
Ok(Self(self.0.try_add(duration)?))
}
pub fn clamp_add<D: Copy + borrow::Borrow<time::Duration>>(self, duration: D) -> Self {
Self(self.0.clamp_add(duration))
}
pub fn checked_sub(self, duration: time::Duration) -> Option<Self> {
self.0
.get()
.checked_sub(duration)
.map(|at| Self(self.0.at(at)))
}
pub fn is_due(self, now: Moment<'d>) -> bool {
self.0.get() <= now.0.get()
}
pub fn try_left(self, now: Moment<'d>) -> Option<time::Duration> {
self.0.get().checked_duration_since(now.0.get())
}
pub const fn into_raw(self) -> time::Instant {
self.0.get()
}
}
impl From<Moment<'_>> for time::Instant {
fn from(moment: Moment<'_>) -> Self {
moment.0.at
}
}
impl<'d> From<Moment<'d>> for Deadline<'d> {
fn from(moment: Moment<'d>) -> Self {
Self(moment.0)
}
}
impl<'d> PartialEq<Moment<'d>> for Deadline<'d> {
fn eq(&self, other: &Moment<'d>) -> bool {
self.0.get() == other.0.get()
}
}
impl<'d> PartialOrd<Moment<'d>> for Deadline<'d> {
fn partial_cmp(&self, other: &Moment<'d>) -> Option<cmp::Ordering> {
self.0.get().partial_cmp(&other.0.get())
}
}
impl<'d> PartialEq<Deadline<'d>> for Moment<'d> {
fn eq(&self, other: &Deadline<'d>) -> bool {
self.0.get() == other.0.get()
}
}
impl<'d> PartialOrd<Deadline<'d>> for Moment<'d> {
fn partial_cmp(&self, other: &Deadline<'d>) -> Option<cmp::Ordering> {
self.0.get().partial_cmp(&other.0.get())
}
}
const _: () = {
assert!(mem::size_of::<Scope<'static>>() == 0);
assert!(mem::size_of::<Origin>() == mem::size_of::<time::Instant>());
assert!(mem::align_of::<Origin>() == mem::align_of::<time::Instant>());
assert!(!mem::needs_drop::<Origin>());
assert!(mem::size_of::<Clock<'static, 'static>>() == mem::size_of::<usize>());
assert!(mem::align_of::<Clock<'static, 'static>>() == mem::align_of::<usize>());
assert!(mem::size_of::<Moment<'static>>() == mem::size_of::<time::Instant>());
assert!(mem::align_of::<Moment<'static>>() == mem::align_of::<time::Instant>());
assert!(mem::size_of::<Deadline<'static>>() == mem::size_of::<time::Instant>());
assert!(mem::align_of::<Deadline<'static>>() == mem::align_of::<time::Instant>());
assert!(!mem::needs_drop::<Moment<'static>>());
assert!(!mem::needs_drop::<Deadline<'static>>());
assert!(
mem::size_of::<At<'static, &'static mut ()>>()
== mem::size_of::<&mut ()>() + mem::size_of::<Moment<'static>>()
);
assert!(mem::align_of::<At<'static, &'static mut ()>>() == mem::align_of::<time::Instant>());
assert!(!mem::needs_drop::<At<'static, &'static mut ()>>());
assert!(mem::size_of::<Bound<'static, time::Instant>>() == mem::size_of::<time::Instant>());
assert!(mem::align_of::<Bound<'static, time::Instant>>() == mem::align_of::<time::Instant>());
assert!(mem::size_of::<Sample>() == mem::size_of::<time::Instant>());
};