gpui-rhai 0.1.8

A Rhai-authored declarative UI runtime for GPUI
Documentation
use std::cell::Cell;
use std::fmt;
use std::rc::Rc;
use std::time::{Duration, Instant};

pub trait RuntimeClockSource {
    fn now(&self) -> Instant;

    fn advance(&self, _duration: Duration) -> bool {
        false
    }
}

#[derive(Clone)]
pub struct RuntimeClock {
    source: Rc<dyn RuntimeClockSource>,
    origin: Instant,
}

impl RuntimeClock {
    #[must_use]
    pub fn system() -> Self {
        Self::from_source(SystemRuntimeClock)
    }

    #[must_use]
    pub fn from_source(source: impl RuntimeClockSource + 'static) -> Self {
        let source: Rc<dyn RuntimeClockSource> = Rc::new(source);
        let origin = source.now();
        Self { source, origin }
    }

    #[must_use]
    pub fn now(&self) -> Instant {
        self.source.now()
    }

    #[must_use]
    pub fn elapsed(&self) -> Duration {
        self.now().saturating_duration_since(self.origin)
    }

    #[must_use]
    pub fn advance(&self, duration: Duration) -> bool {
        self.source.advance(duration)
    }
}

impl Default for RuntimeClock {
    fn default() -> Self {
        Self::system()
    }
}

impl fmt::Debug for RuntimeClock {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("RuntimeClock(..)")
    }
}

#[derive(Clone, Copy, Debug)]
struct SystemRuntimeClock;

impl RuntimeClockSource for SystemRuntimeClock {
    fn now(&self) -> Instant {
        Instant::now()
    }
}

#[derive(Clone, Debug)]
pub struct ManualRuntimeClock {
    now: Rc<Cell<Instant>>,
}

impl ManualRuntimeClock {
    #[must_use]
    pub fn new(now: Instant) -> Self {
        Self {
            now: Rc::new(Cell::new(now)),
        }
    }

    #[must_use]
    pub fn clock(&self) -> RuntimeClock {
        RuntimeClock::from_source(self.clone())
    }

    pub fn advance(&self, duration: Duration) {
        self.now.set(self.now.get() + duration);
    }

    pub fn set(&self, now: Instant) {
        self.now.set(now);
    }
}

impl RuntimeClockSource for ManualRuntimeClock {
    fn now(&self) -> Instant {
        self.now.get()
    }

    fn advance(&self, duration: Duration) -> bool {
        self.advance(duration);
        true
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn manual_clock_is_shared_and_monotonic_by_host_policy() {
        let start = Instant::now();
        let manual = ManualRuntimeClock::new(start);
        let clock = manual.clock();
        manual.advance(Duration::from_millis(16));
        assert_eq!(clock.now(), start + Duration::from_millis(16));
        assert_eq!(clock.elapsed(), Duration::from_millis(16));
    }
}