microlock 0.3.1

A crate for waiting: Small locks and other timing things!
Documentation
use std::{
    cmp::Ordering,
    time::{Duration, Instant},
};

/// Our real representation of an Infinite duration.
pub const INFINITE_DURATION: Duration = Duration::new(u64::MAX, 1_000_000_000 - 1);

#[derive(Clone, Copy, PartialEq, Eq)]
/// A duration wrapper supporting elapsed/negative, real, and infinite
/// durations. Due to the solar system's impending collapse before the 64 bit
/// integer limit of seconds is reached, an Infinite duration is translated
/// to that when turning this back info a normal duration. If you run your
/// program for longer than the sun's entire lifespan including the past, this
/// may cause an issue. But I think you'll have much bigger issues long before
/// that, including the fact that you even came up with that idea.
pub enum TimerDuration {
    Elapsed,
    Real(Duration),
    Infinite,
}
impl TimerDuration {
    /// Takes the difference between a TimerDuration a and a Duration b,
    /// resulting in either a negative (Elapsed) duration or a Real one.
    /// The Infinite duration is passed through as-is, with no subtraction
    /// done to it.
    pub fn from_difference(a: TimerDuration, b: Duration) -> TimerDuration {
        if a == TimerDuration::Infinite {
            return a;
        }
        let a = a.to_real();
        if a < b {
            TimerDuration::Elapsed
        } else {
            TimerDuration::Real(a - b)
        }
    }

    /// Converts this into a Duration that is waitable by rust's standard
    /// functions, for use in e.g. specifying timeouts to std operations.
    pub fn to_real(&self) -> Duration {
        match *self {
            Self::Real(d) if d > Duration::ZERO => d,
            Self::Infinite => INFINITE_DURATION,
            _ => Duration::new(0, 0),
        }
    }

    pub const fn is_elapsed(&self) -> bool {
        matches!(self, TimerDuration::Elapsed)
    }

    pub const fn is_real(&self) -> bool {
        matches!(self, TimerDuration::Real(_))
    }

    pub const fn is_infinite(&self) -> bool {
        matches!(self, TimerDuration::Infinite)
    }
}
impl From<Duration> for TimerDuration {
    fn from(value: Duration) -> Self {
        if value == Duration::ZERO {
            return Self::Elapsed;
        }
        if value == INFINITE_DURATION {
            return Self::Infinite;
        }
        Self::Real(value)
    }
}
impl From<u64> for TimerDuration {
    fn from(value: u64) -> Self {
        if value == 0 {
            return Self::Elapsed;
        }
        if value == u64::MAX {
            return Self::Infinite;
        }
        Self::Real(Duration::from_millis(value))
    }
}
impl PartialOrd for TimerDuration {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for TimerDuration {
    fn cmp(&self, other: &Self) -> Ordering {
        match (self, other) {
            (TimerDuration::Elapsed, TimerDuration::Elapsed) => Ordering::Equal,
            (TimerDuration::Elapsed, _) => Ordering::Less,
            (TimerDuration::Real(_), TimerDuration::Elapsed) => Ordering::Greater,
            (TimerDuration::Real(a), TimerDuration::Real(b)) => a.cmp(b),
            (TimerDuration::Real(_), TimerDuration::Infinite) => Ordering::Less,
            (TimerDuration::Infinite, TimerDuration::Infinite) => Ordering::Equal,
            (TimerDuration::Infinite, _) => Ordering::Greater,
        }
    }
}

#[derive(Clone, Copy)]
/// Holds a start time and a TimerDuration and allows for the time elapsed
/// and the time left to be queried. Timers are immutable.
pub struct Timer(Option<Instant>, TimerDuration);
impl Timer {
    pub fn new(d: TimerDuration) -> Self {
        Self(Some(Instant::now()), d)
    }

    pub const fn new_const(d: TimerDuration) -> Self {
        if d.is_real() {
            panic!("TimerDuration is only const-compatible when Elapsed or Infinite.");
        }
        Self(None, d)
    }

    pub fn time_elapsed(&self) -> Duration {
        self.0.unwrap_or_else(Instant::now).elapsed()
    }

    pub fn restart(&mut self) {
        self.0 = Some(Instant::now());
    }

}

/// Something with a timeout.
pub trait Timed {
    /// Returns if the time has elapsed or not.
    fn has_elapsed(&self) -> bool;
    /// Returns how much time is left. Unbounded and elapsed durations use
    /// Infinite and Elapsed respectively. Others always return Real.
    fn time_left(&self) -> TimerDuration;
}
impl Timed for Timer {
    fn has_elapsed(&self) -> bool {
        if self.1.is_elapsed() {
            return true;
        }
        if self.1.is_infinite() {
            return false;
        }
        self.0.unwrap().elapsed() >= self.1.to_real()
    }
    fn time_left(&self) -> TimerDuration {
        if !self.1.is_real() {
            return self.1;
        }
        TimerDuration::from_difference(self.1, self.0.unwrap().elapsed())
    }
}