use std::fmt;
use crate::nillable::*;
#[derive(Debug, Clone, Copy, Default)]
pub struct O<T> {
pub current: Nillable<T>,
}
#[derive(Debug, Clone, Default)]
pub struct S<N, T> {
pub current: Nillable<T>,
pub previous: N,
}
impl<T: fmt::Display> fmt::Display for O<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.current)
}
}
impl<N: fmt::Display, T: fmt::Display> fmt::Display for S<N, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}->{}", self.previous, self.current)
}
}
pub(crate) trait Sealed: Sized {}
impl Sealed for i64 {}
impl Sealed for f64 {}
impl Sealed for bool {}
trait Counting<T>: Sized {}
impl<T> Counting<T> for O<T> where T: Sealed {}
impl<N, T> Counting<T> for S<N, T>
where
N: Counting<T>,
T: Sealed,
{
}
macro_rules! o {
($e:expr) => {
O { current: $e }
};
}
macro_rules! s {
($e:expr, $n:expr) => {
S {
current: $e,
previous: $n,
}
};
}
#[allow(private_bounds)]
pub trait Ago<T>: Counting<T> {
fn ago(&self, dt: usize) -> &Nillable<T>;
}
impl<T> Ago<T> for O<T>
where
T: Sealed,
{
#[track_caller]
#[inline(always)]
fn ago(&self, dt: usize) -> &Nillable<T> {
match dt {
0 => panic!(
"Cannot look into the past at distance 0, use the current environment instead"
),
1 => &self.current,
_ => panic!("Tried to look too much into the past"),
}
}
}
impl<N, T> Ago<T> for S<N, T>
where
T: Sealed,
N: Ago<T>,
{
#[track_caller]
#[inline(always)]
fn ago(&self, dt: usize) -> &Nillable<T> {
match dt {
0 => panic!(
"Cannot look into the past at distance 0, use the current environment instead"
),
1 => &self.current,
dt => self.previous.ago(dt - 1),
}
}
}
trait Downcast<N, T>: Counting<T> {
fn downcast(self) -> N;
}
impl<T> Downcast<O<T>, T> for S<O<T>, T>
where
T: Sealed,
{
#[inline(always)]
fn downcast(self) -> O<T> {
o!(self.current)
}
}
impl<N, T> Downcast<S<N, T>, T> for S<S<N, T>, T>
where
T: Sealed,
N: Counting<T>,
S<N, T>: Downcast<N, T>,
{
#[inline(always)]
fn downcast(self) -> S<N, T> {
s!(self.current, self.previous.downcast())
}
}
#[allow(private_bounds)]
pub trait Extend<N, T>: Sized {
fn extend(self, new: Nillable<T>) -> N;
fn extend_undefined(self) -> N {
self.extend(Nillable::Nil)
}
}
impl<T> Extend<S<O<T>, T>, T> for O<T> {
#[inline(always)]
fn extend(self, new: Nillable<T>) -> S<O<T>, T> {
s!(new, self)
}
}
impl<N, T> Extend<S<S<N, T>, T>, T> for S<N, T>
where
N: Extend<S<N, T>, T>,
{
#[inline(always)]
fn extend(self, new: Nillable<T>) -> S<S<N, T>, T> {
s!(new, self.previous.extend(self.current))
}
}
#[allow(private_bounds)]
pub trait Update<T>: Counting<T> {
fn update(self, new: Nillable<T>) -> Self;
fn update_mut(&mut self, new: Nillable<T>) {
replace_with::replace_with_or_abort(self, |s| s.update(new));
}
fn update_undefined(self) -> Self {
self.update(Nillable::Nil)
}
fn update_mut_undefined(&mut self) {
self.update_mut(Nillable::Nil);
}
}
impl<T> Update<T> for O<T>
where
T: Sealed,
{
#[inline(always)]
fn update(self, new: Nillable<T>) -> Self {
Self { current: new }
}
}
impl<N, T> Update<T> for S<N, T>
where
T: Sealed,
N: Counting<T>,
S<N, T>: Downcast<N, T>,
{
#[inline(always)]
fn update(self, new: Nillable<T>) -> Self {
s!(new, self.downcast())
}
}
#[allow(private_bounds)]
pub trait IntoHistory: Sealed {
#[inline(always)]
fn into_history(self) -> O<Self> {
o!(Defined(self))
}
#[inline(always)]
fn empty_history() -> O<Self> {
o!(Nillable::Nil)
}
}
impl<T> IntoHistory for T where T: Sealed {}