diff --git a/crates/rune-modules/src/time.rs b/crates/rune-modules/src/time.rs index 6260844ce..99ff2f632 100644 --- a/crates/rune-modules/src/time.rs +++ b/crates/rune-modules/src/time.rs @@ -29,53 +29,1287 @@ //! } //! ``` -use rune::{Any, ContextError, Module}; +use core::cmp::Ordering; +use core::hash::Hash; + +use rune::alloc::fmt::TryWrite; +use rune::runtime::{Formatter, Hasher, Mut, VmResult}; +use rune::{docstring, item, vm_panic, Any, ContextError, Module, ToConstValue}; + +const NANOS_PER_SEC: u32 = 1_000_000_000; /// Construct the `time` module. pub fn module(_stdio: bool) -> Result { - let mut module = Module::with_crate("time")?; - module.ty::()?; - module.function_meta(Duration::from_secs__meta)?; - module.function_meta(sleep)?; - Ok(module) + let mut m = Module::with_crate("time")?; + + m.function_meta(sleep)?; + m.function_meta(interval)?; + m.function_meta(interval_at)?; + + m.ty::()?; + + m.function_meta(Duration::new__meta)?; + m.function_meta(Duration::from_secs__meta)?; + m.function_meta(Duration::from_millis__meta)?; + m.function_meta(Duration::from_micros__meta)?; + m.function_meta(Duration::from_nanos__meta)?; + m.function_meta(Duration::is_zero__meta)?; + m.function_meta(Duration::as_secs__meta)?; + m.function_meta(Duration::subsec_millis__meta)?; + m.function_meta(Duration::subsec_micros__meta)?; + m.function_meta(Duration::subsec_nanos__meta)?; + m.function_meta(Duration::as_millis__meta)?; + m.function_meta(Duration::as_micros__meta)?; + m.function_meta(Duration::as_nanos__meta)?; + m.function_meta(Duration::as_secs_f64__meta)?; + m.function_meta(Duration::from_secs_f64__meta)?; + m.function_meta(Duration::add__meta)?; + m.function_meta(Duration::add_assign__meta)?; + m.function_meta(Duration::partial_eq__meta)?; + m.implement_trait::(item!(::std::cmp::PartialEq))?; + m.function_meta(Duration::eq__meta)?; + m.implement_trait::(item!(::std::cmp::Eq))?; + m.function_meta(Duration::partial_cmp__meta)?; + m.implement_trait::(item!(::std::cmp::PartialOrd))?; + m.function_meta(Duration::cmp__meta)?; + m.implement_trait::(item!(::std::cmp::Ord))?; + m.function_meta(Duration::hash__meta)?; + m.function_meta(Duration::string_debug__meta)?; + m.function_meta(Duration::clone__meta)?; + m.implement_trait::(item!(::std::clone::Clone))?; + + m.constant( + "SECOND", + Duration { + inner: tokio::time::Duration::from_secs(1), + }, + ) + .build_associated::()? + .docs(docstring! { + /// The duration of one second. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::SECOND; + /// ``` + })?; + + m.constant( + "MILLISECOND", + Duration { + inner: tokio::time::Duration::from_millis(1), + }, + ) + .build_associated::()? + .docs(docstring! { + /// The duration of one millisecond. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::MILLISECOND; + /// ``` + })?; + + m.constant( + "MICROSECOND", + Duration { + inner: tokio::time::Duration::from_micros(1), + }, + ) + .build_associated::()? + .docs(docstring! { + /// The duration of one microsecond. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::MICROSECOND; + /// ``` + })?; + + m.constant( + "NANOSECOND", + Duration { + inner: tokio::time::Duration::from_nanos(1), + }, + ) + .build_associated::()? + .docs(docstring! { + /// The duration of one nanosecond. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::NANOSECOND; + /// ``` + })?; + + m.constant( + "ZERO", + Duration { + inner: tokio::time::Duration::ZERO, + }, + ) + .build_associated::()? + .docs(docstring! { + /// A duration of zero time. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::ZERO; + /// ``` + })?; + + m.constant( + "MAX", + Duration { + inner: tokio::time::Duration::MAX, + }, + ) + .build_associated::()? + .docs(docstring! { + /// The maximum duration. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::MAX; + /// assert!(Duration::ZERO < Duration::MAX); + /// ``` + })?; + + m.ty::()?; + m.function_meta(Instant::now__meta)?; + m.function_meta(Instant::duration_since__meta)?; + m.function_meta(Instant::elapsed__meta)?; + m.function_meta(Instant::add__meta)?; + m.function_meta(Instant::add_assign__meta)?; + m.function_meta(Instant::partial_eq__meta)?; + m.implement_trait::(item!(::std::cmp::PartialEq))?; + m.function_meta(Instant::eq__meta)?; + m.implement_trait::(item!(::std::cmp::Eq))?; + m.function_meta(Instant::partial_cmp__meta)?; + m.implement_trait::(item!(::std::cmp::PartialOrd))?; + m.function_meta(Instant::cmp__meta)?; + m.implement_trait::(item!(::std::cmp::Ord))?; + m.function_meta(Instant::hash__meta)?; + m.function_meta(Instant::string_debug__meta)?; + m.function_meta(Instant::clone__meta)?; + m.implement_trait::(item!(::std::clone::Clone))?; + + m.ty::()?; + m.function("tick", Interval::tick) + .build_associated::()?; + m.function_meta(Interval::reset__meta)?; + m.function_meta(Interval::reset_immediately__meta)?; + m.function_meta(Interval::reset_after__meta)?; + m.function_meta(Interval::reset_at__meta)?; + + Ok(m) +} + +/// Waits until duration has elapsed. +/// +/// # Examples +/// +/// ```rune,no_run +/// use time::Duration; +/// +/// let duration = Duration::from_secs(10); +/// time::sleep(duration).await; +/// println!("Surprise!"); +/// ``` +#[rune::function] +async fn sleep(duration: Duration) { + tokio::time::sleep(duration.inner).await; +} + +/// Creates new [`Interval`] that yields with interval of `period`. The first +/// tick completes immediately. +/// +/// An interval will tick indefinitely. At any time, the [`Interval`] value can +/// be dropped. This cancels the interval. +/// +/// # Examples +/// +/// ```rune,no_run +/// use time::Duration; +/// +/// let duration = Duration::from_millis(10); +/// let interval = time::interval(duration); +/// +/// interval.tick().await; // ticks immediately +/// interval.tick().await; // ticks after 10ms +/// interval.tick().await; // ticks after 10ms +/// +/// println!("approximately 20ms have elapsed..."); +/// ``` +#[rune::function] +async fn interval(period: Duration) -> Interval { + Interval { + inner: tokio::time::interval(period.inner), + } +} + +/// Creates new [`Interval`] that yields with interval of `period` with the +/// first tick completing at `start`. +/// +/// An interval will tick indefinitely. At any time, the [`Interval`] value can +/// be dropped. This cancels the interval. +/// +/// # Vm Panics +/// +/// This function panics if `period` is zero. +/// +/// # Examples +/// +/// ```rune,no_run +/// use time::{Duration, Instant}; +/// +/// let start = Instant::now() + Duration::from_millis(50); +/// let interval = time::interval_at(start, Duration::from_millis(10)); +/// +/// interval.tick().await; // ticks after 50ms +/// interval.tick().await; // ticks after 10ms +/// interval.tick().await; // ticks after 10ms +/// +/// println!("approximately 70ms have elapsed..."); +/// ``` +#[rune::function] +async fn interval_at(start: Instant, period: Duration) -> Interval { + Interval { + inner: tokio::time::interval_at(start.inner, period.inner), + } } -#[derive(Debug, Clone, Copy, Any)] +/// A `Duration` type to represent a span of time, typically used for system +/// timeouts. +/// +/// Each `Duration` is composed of a whole number of seconds and a fractional part +/// represented in nanoseconds. If the underlying system does not support +/// nanosecond-level precision, APIs binding a system timeout will typically round up +/// the number of nanoseconds. +/// +/// # Examples +/// +/// ```rune +/// use time::Duration; +/// +/// let five_seconds = Duration::new(5, 0); +/// let five_seconds_and_five_nanos = five_seconds + Duration::new(0, 5); +/// +/// assert_eq!(five_seconds_and_five_nanos.as_secs(), 5); +/// assert_eq!(five_seconds_and_five_nanos.subsec_nanos(), 5); +/// +/// let ten_millis = Duration::from_millis(10); +/// ``` +#[derive(Debug, Clone, Copy, Any, ToConstValue)] #[rune(item = ::time)] -struct Duration { +pub struct Duration { + #[const_value(with = self::const_duration)] inner: tokio::time::Duration, } impl Duration { - /// Construct a duration from the given number of seconds. + /// Converts [`Duration`] into a [`std::time::Duration`]. + pub fn into_std(self) -> std::time::Duration { + std::time::Duration::new(self.inner.as_secs(), self.inner.subsec_nanos()) + } + + /// Creates a [`Duration`] from a [`std::time::Duration`]. + pub fn from_std(duration: std::time::Duration) -> Self { + Self { + inner: tokio::time::Duration::new(duration.as_secs(), duration.subsec_nanos()), + } + } + + /// Converts [`Duration`] into a [`tokio::time::Duration`]. + /// + /// # Example + /// + /// ``` + /// use rune_modules::time::Duration; + /// + /// let duration = Duration::from_secs(5); + /// let tokio_duration = duration.into_tokio(); + /// ``` + pub fn into_tokio(self) -> tokio::time::Duration { + self.inner + } + + /// Creates a [`Duration`] from a [`tokio::time::Duration`]. + /// + /// # Example + /// + /// ``` + /// use rune_modules::time::Duration; + /// + /// let tokio_duration = tokio::time::Duration::from_secs(5); + /// let duration = Duration::from_tokio(tokio_duration); + /// ``` + pub fn from_tokio(duration: tokio::time::Duration) -> Self { + Self { inner: duration } + } + + /// Creates a new `Duration` from the specified number of whole seconds and + /// additional nanoseconds. + /// + /// If the number of nanoseconds is greater than 1 billion (the number of + /// nanoseconds in a second), then it will carry over into the seconds provided. + /// + /// # Vm Panics + /// + /// This constructor will panic if the carry from the nanoseconds overflows + /// the seconds counter. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let five_seconds = Duration::new(5, 0); + /// ``` + #[rune::function(keep, path = Self::new)] + pub fn new(secs: u64, nanos: u32) -> VmResult { + if nanos >= NANOS_PER_SEC && secs.checked_add((nanos / NANOS_PER_SEC) as u64).is_none() { + vm_panic!("overflow in Duration::new"); + } + + VmResult::Ok(Self { + inner: tokio::time::Duration::new(secs, nanos), + }) + } + + /// Creates a new `Duration` from the specified number of whole seconds. /// /// # Examples /// /// ```rune /// use time::Duration; /// - /// let d = Duration::from_secs(10); + /// let duration = Duration::from_secs(5); /// ``` #[rune::function(keep, path = Self::from_secs)] - fn from_secs(secs: u64) -> Self { + pub const fn from_secs(secs: u64) -> Self { Self { inner: tokio::time::Duration::from_secs(secs), } } -} -/// Sleep for the given [`Duration`]. -/// -/// # Examples -/// -/// ```rune,no_run -/// use time::Duration; -/// -/// let d = Duration::from_secs(10); -/// time::sleep(d).await; -/// println!("Surprise!"); -/// ``` -#[rune::function] -async fn sleep(duration: Duration) { - tokio::time::sleep(duration.inner).await; + /// Creates a new `Duration` from the specified number of milliseconds. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::from_millis(2569); + /// ``` + #[rune::function(keep, path = Self::from_millis)] + pub const fn from_millis(millis: u64) -> Self { + Self { + inner: tokio::time::Duration::from_millis(millis), + } + } + + /// Creates a new `Duration` from the specified number of microseconds. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::from_micros(1_000_002); + /// ``` + #[rune::function(keep, path = Self::from_micros)] + #[inline] + pub const fn from_micros(micros: u64) -> Self { + Self { + inner: tokio::time::Duration::from_micros(micros), + } + } + + /// Creates a new `Duration` from the specified number of nanoseconds. + /// + /// Note: Using this on the return value of `as_nanos()` might cause unexpected behavior: + /// `as_nanos()` returns a u128, and can return values that do not fit in u64, e.g. 585 years. + /// Instead, consider using the pattern `Duration::new(d.as_secs(), d.subsec_nanos())` + /// if you cannot copy/clone the Duration directly. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::from_nanos(1_000_000_123); + /// ``` + #[rune::function(keep, path = Self::from_nanos)] + #[inline] + pub const fn from_nanos(nanos: u64) -> Self { + Self { + inner: tokio::time::Duration::from_nanos(nanos), + } + } + + /// Returns true if this `Duration` spans no time. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// assert!(Duration::ZERO.is_zero()); + /// assert!(Duration::new(0, 0).is_zero()); + /// assert!(Duration::from_nanos(0).is_zero()); + /// assert!(Duration::from_secs(0).is_zero()); + /// + /// assert!(!Duration::new(1, 1).is_zero()); + /// assert!(!Duration::from_nanos(1).is_zero()); + /// assert!(!Duration::from_secs(1).is_zero()); + /// ``` + #[rune::function(keep)] + #[inline] + pub const fn is_zero(&self) -> bool { + self.inner.is_zero() + } + + /// Returns the number of _whole_ seconds contained by this `Duration`. + /// + /// The returned value does not include the fractional (nanosecond) part of + /// the duration, which can be obtained using [`subsec_nanos`]. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::new(5, 730023852); + /// assert_eq!(duration.as_secs(), 5); + /// ``` + /// + /// To determine the total number of seconds represented by the `Duration` + /// including the fractional part, use [`as_secs_f64`] or [`as_secs_f32`] + /// + /// [`as_secs_f64`]: Duration::as_secs_f64 + /// [`as_secs_f32`]: Duration::as_secs_f32 + /// [`subsec_nanos`]: Duration::subsec_nanos + #[rune::function(keep)] + #[inline] + pub const fn as_secs(&self) -> u64 { + self.inner.as_secs() + } + + /// Returns the fractional part of this `Duration`, in whole milliseconds. + /// + /// This method does **not** return the length of the duration when + /// represented by milliseconds. The returned number always represents a + /// fractional portion of a second (i.e., it is less than one thousand). + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::from_millis(5432); + /// assert_eq!(duration.as_secs(), 5); + /// assert_eq!(duration.subsec_millis(), 432); + /// ``` + #[rune::function(keep)] + #[inline] + pub const fn subsec_millis(&self) -> u32 { + self.inner.subsec_millis() + } + + /// Returns the fractional part of this `Duration`, in whole microseconds. + /// + /// This method does **not** return the length of the duration when + /// represented by microseconds. The returned number always represents a + /// fractional portion of a second (i.e., it is less than one million). + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::from_micros(1_234_567); + /// assert_eq!(duration.as_secs(), 1); + /// assert_eq!(duration.subsec_micros(), 234_567); + /// ``` + #[rune::function(keep)] + #[inline] + pub const fn subsec_micros(&self) -> u32 { + self.inner.subsec_micros() + } + + /// Returns the fractional part of this `Duration`, in nanoseconds. + /// + /// This method does **not** return the length of the duration when + /// represented by nanoseconds. The returned number always represents a + /// fractional portion of a second (i.e., it is less than one billion). + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::from_millis(5010); + /// assert_eq!(duration.as_secs(), 5); + /// assert_eq!(duration.subsec_nanos(), 10_000_000); + /// ``` + #[rune::function(keep)] + #[inline] + pub const fn subsec_nanos(&self) -> u32 { + self.inner.subsec_nanos() + } + + /// Returns the total number of whole milliseconds contained by this + /// `Duration`. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::new(5, 730023852); + /// assert_eq!(duration.as_millis(), 5730); + /// ``` + #[rune::function(keep)] + #[inline] + pub const fn as_millis(&self) -> u128 { + self.inner.as_millis() + } + + /// Returns the total number of whole microseconds contained by this + /// `Duration`. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::new(5, 730023852); + /// assert_eq!(duration.as_micros(), 5730023); + /// ``` + #[rune::function(keep)] + #[inline] + pub const fn as_micros(&self) -> u128 { + self.inner.as_micros() + } + + /// Returns the total number of nanoseconds contained by this `Duration`. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::new(5, 730023852); + /// assert_eq!(duration.as_nanos(), 5730023852); + /// ``` + #[rune::function(keep)] + #[inline] + pub const fn as_nanos(&self) -> u128 { + self.inner.as_nanos() + } + + /// Returns the number of seconds contained by this `Duration` as `f64`. + /// + /// The returned value does include the fractional (nanosecond) part of the + /// duration. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::from_secs(60).as_secs_f64(); + /// ``` + #[rune::function(keep)] + #[inline] + pub fn as_secs_f64(&self) -> f64 { + self.inner.as_secs_f64() + } + + /// Creates a new `Duration` from the specified number of seconds represented + /// as `f64`. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let duration = Duration::from_secs_f64(0.0); + /// ``` + #[rune::function(keep, path = Self::from_secs_f64)] + pub fn from_secs_f64(secs: f64) -> VmResult { + match tokio::time::Duration::try_from_secs_f64(secs) { + Ok(duration) => VmResult::Ok(Self { inner: duration }), + Err(e) => vm_panic!(e), + } + } + + /// Add a duration to this instant and return a new instant. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let first = Duration::SECOND; + /// let second = first + Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = ADD)] + #[inline] + fn add(&self, rhs: &Duration) -> VmResult { + let Some(inner) = self.inner.checked_add(rhs.inner) else { + vm_panic!("overflow when adding durations") + }; + + VmResult::Ok(Self { inner }) + } + + /// Add a duration to this instant and return a new instant. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let first = Duration::SECOND; + /// let second = first.clone(); + /// second += Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = ADD_ASSIGN)] + #[inline] + fn add_assign(&mut self, rhs: &Duration) -> VmResult<()> { + let Some(inner) = self.inner.checked_add(rhs.inner) else { + vm_panic!("overflow when adding duration to instant") + }; + + self.inner = inner; + VmResult::Ok(()) + } + + /// Test two durations for partial equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::partial_eq; + /// + /// use time::Duration; + /// + /// let millis = Duration::MILLISECOND; + /// let second = Duration::SECOND; + /// + /// assert_eq!(partial_eq(millis, millis), true); + /// assert_eq!(partial_eq(millis, second), false); + /// assert_eq!(partial_eq(second, millis), false); + /// ``` + #[rune::function(keep, instance, protocol = PARTIAL_EQ)] + #[inline] + fn partial_eq(&self, rhs: &Self) -> bool { + PartialEq::eq(&self.inner, &rhs.inner) + } + + /// Test two durations for total equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// + /// use time::Duration; + /// + /// let millis = Duration::MILLISECOND; + /// let second = Duration::SECOND; + /// + /// assert_eq!(eq(millis, millis), true); + /// assert_eq!(eq(millis, second), false); + /// assert_eq!(eq(second, millis), false); + /// ``` + #[rune::function(keep, instance, protocol = EQ)] + #[inline] + fn eq(&self, rhs: &Self) -> bool { + PartialEq::eq(&self.inner, &rhs.inner) + } + + /// Perform a partial ordered comparison between two durations. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let millis = Duration::MILLISECOND; + /// let second = Duration::SECOND; + /// + /// assert!(millis < second); + /// assert!(second > millis); + /// assert!(millis == millis); + /// ``` + /// + /// Using explicit functions: + /// + /// ```rune + /// use std::cmp::Ordering; + /// use std::ops::partial_cmp; + /// + /// use time::Duration; + /// + /// let millis = Duration::MILLISECOND; + /// let second = Duration::SECOND; + /// + /// assert_eq!(partial_cmp(millis, second), Some(Ordering::Less)); + /// assert_eq!(partial_cmp(second, millis), Some(Ordering::Greater)); + /// assert_eq!(partial_cmp(millis, millis), Some(Ordering::Equal)); + /// ``` + #[rune::function(keep, instance, protocol = PARTIAL_CMP)] + #[inline] + fn partial_cmp(&self, rhs: &Self) -> Option { + PartialOrd::partial_cmp(&self.inner, &rhs.inner) + } + + /// Perform a totally ordered comparison between two durations. + /// + /// # Examples + /// + /// ```rune + /// use std::cmp::Ordering; + /// use std::ops::cmp; + /// + /// use time::Duration; + /// + /// let millis = Duration::MILLISECOND; + /// let second = Duration::SECOND; + /// + /// assert_eq!(cmp(millis, second), Ordering::Less); + /// assert_eq!(cmp(second, millis), Ordering::Greater); + /// assert_eq!(cmp(millis, millis), Ordering::Equal); + /// ``` + #[rune::function(keep, instance, protocol = CMP)] + #[inline] + fn cmp(&self, rhs: &Self) -> Ordering { + Ord::cmp(&self.inner, &rhs.inner) + } + + /// Hash the duration. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::hash; + /// + /// use time::Duration; + /// + /// let second = Duration::SECOND; + /// + /// assert_eq!(hash(second), hash(second)); + /// ``` + #[rune::function(keep, instance, protocol = HASH)] + fn hash(&self, hasher: &mut Hasher) { + self.inner.hash(hasher); + } + + /// Write a debug representation of the duration. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let second = Duration::SECOND; + /// + /// println!("{second:?}"); + /// ``` + #[rune::function(keep, instance, protocol = STRING_DEBUG)] + fn string_debug(&self, f: &mut Formatter) -> VmResult<()> { + rune::vm_write!(f, "{:?}", self.inner) + } + + /// Clone the current duration. + /// + /// # Examples + /// + /// ```rune + /// use time::Duration; + /// + /// let first = Duration::SECOND; + /// let second = Duration::SECOND; + /// second += Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = CLONE)] + fn clone(&self) -> Self { + Self { inner: self.inner } + } +} + +mod const_duration { + use rune::runtime::{ConstValue, RuntimeError, Value}; + use tokio::time::Duration; + + #[inline] + pub(super) fn to_const_value(duration: Duration) -> Result { + let secs = duration.as_secs(); + let nanos = duration.subsec_nanos(); + rune::to_const_value((secs, nanos)) + } + + #[inline] + pub(super) fn from_const_value(value: &ConstValue) -> Result { + let (secs, nanos) = rune::from_const_value::<(u64, u32)>(value)?; + Ok(Duration::new(secs, nanos)) + } + + #[inline] + pub(super) fn from_value(value: Value) -> Result { + let (secs, nanos) = rune::from_value::<(u64, u32)>(value)?; + Ok(Duration::new(secs, nanos)) + } +} + +/// Interval returned by [`interval`] and [`interval_at`]. +/// +/// This type allows you to wait on a sequence of instants with a certain +/// duration between each instant. Unlike calling [`sleep`] in a loop, this lets +/// you count the time spent between the calls to [`sleep`] as well. +#[derive(Debug, Any)] +#[rune(item = ::time)] +pub struct Interval { + inner: tokio::time::Interval, +} + +impl Interval { + /// Completes when the next instant in the interval has been reached. + /// + /// # Cancel safety + /// + /// This method is cancellation safe. If `tick` is used as the branch in a `select` and + /// another branch completes first, then no tick has been consumed. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let interval = time::interval(Duration::from_millis(10)); + /// + /// interval.tick().await; + /// println!("approximately 0ms have elapsed. The first tick completes immediately."); + /// interval.tick().await; + /// interval.tick().await; + /// + /// println!("approximately 20ms have elapsed..."); + /// ``` + pub async fn tick(mut internal: Mut) { + internal.inner.tick().await; + } + + /// Resets the interval to complete one period after the current time. + /// + /// This is equivalent to calling `reset_at(Instant::now() + period)`. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let interval = time::interval(Duration::from_millis(100)); + /// interval.tick().await; + /// + /// time::sleep(Duration::from_millis(50)).await; + /// interval.reset(); + /// + /// interval.tick().await; + /// interval.tick().await; + /// + /// println!("approximately 250ms have elapsed..."); + /// ``` + #[rune::function(instance, keep)] + fn reset(&mut self) { + self.inner.reset(); + } + + /// Resets the interval immediately. + /// + /// This is equivalent to calling `reset_at(Instant::now())`. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let interval = time::interval(Duration::from_millis(100)); + /// interval.tick().await; + /// + /// time::sleep(Duration::from_millis(50)).await; + /// interval.reset_immediately(); + /// + /// interval.tick().await; + /// interval.tick().await; + /// + /// println!("approximately 150ms have elapsed..."); + /// ``` + #[rune::function(instance, keep)] + fn reset_immediately(&mut self) { + self.inner.reset_immediately(); + } + + /// Resets the interval to complete one period after the current time. + /// + /// This is equivalent to calling `reset_at(Instant::now() + period)`. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let interval = time::interval(Duration::from_millis(100)); + /// interval.tick().await; + /// + /// time::sleep(Duration::from_millis(50)).await; + /// interval.reset(); + /// + /// interval.tick().await; + /// interval.tick().await; + /// + /// println!("approximately 250ms have elapsed..."); + /// ``` + #[rune::function(instance, keep)] + fn reset_after(&mut self, after: Duration) { + self.inner.reset_after(after.inner); + } + + /// Resets the interval to complete one period after the current time. + /// + /// This is equivalent to calling `reset_at(Instant::now() + period)`. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::Duration; + /// + /// let interval = time::interval(Duration::from_millis(100)); + /// interval.tick().await; + /// + /// time::sleep(Duration::from_millis(50)).await; + /// interval.reset(); + /// + /// interval.tick().await; + /// interval.tick().await; + /// + /// println!("approximately 250ms have elapsed..."); + /// ``` + #[rune::function(instance, keep)] + fn reset_at(&mut self, deadline: Instant) { + self.inner.reset_at(deadline.inner); + } +} + +/// A measurement of a monotonically nondecreasing clock. +/// Opaque and useful only with `Duration`. +/// +/// Instants are always guaranteed to be no less than any previously measured +/// instant when created, and are often useful for tasks such as measuring +/// benchmarks or timing how long an operation takes. +/// +/// Note, however, that instants are not guaranteed to be **steady**. In other +/// words, each tick of the underlying clock may not be the same length (e.g. +/// some seconds may be longer than others). An instant may jump forwards or +/// experience time dilation (slow down or speed up), but it will never go +/// backwards. +/// +/// Instants are opaque types that can only be compared to one another. There is +/// no method to get "the number of seconds" from an instant. Instead, it only +/// allows measuring the duration between two instants (or comparing two +/// instants). +/// +/// The size of an `Instant` struct may vary depending on the target operating +/// system. +#[derive(Debug, Any)] +#[rune(item = ::time)] +pub struct Instant { + inner: tokio::time::Instant, +} + +impl Instant { + /// Returns an instant corresponding to `now`. + /// + /// # Examples + /// + /// ```rune + /// use time::{Duration, Instant}; + /// + /// let instant = Instant::now(); + /// ``` + #[rune::function(keep, path = Self::now)] + pub fn now() -> Instant { + Instant { + inner: tokio::time::Instant::now(), + } + } + + /// Returns the amount of time elapsed from another instant to this one, or + /// zero duration if that instant is later than this one. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::{Duration, Instant}; + /// + /// let instant = Instant::now(); + /// + /// let three_secs = Duration::from_secs(3); + /// time::sleep(three_secs).await; + /// + /// let now = Instant::now(); + /// let duration_since = now.duration_since(instant); + /// ``` + #[rune::function(instance, keep)] + pub fn duration_since(&self, earlier: Instant) -> Duration { + Duration { + inner: tokio::time::Instant::duration_since(&self.inner, earlier.inner), + } + } + + /// Returns the amount of time elapsed since this instant was created, or + /// zero duration if that this instant is in the future. + /// + /// # Examples + /// + /// ```rune,no_run + /// use time::{Duration, Instant}; + /// + /// let instant = Instant::now(); + /// + /// let three_secs = Duration::from_secs(3); + /// time::sleep(three_secs).await; + /// + /// let elapsed = instant.elapsed(); + /// ``` + #[rune::function(instance, keep)] + pub fn elapsed(&self) -> Duration { + Duration { + inner: tokio::time::Instant::elapsed(&self.inner), + } + } + + /// Add a duration to this instant and return a new instant. + /// + /// # Examples + /// + /// ```rune + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = ADD)] + #[inline] + fn add(&self, rhs: &Duration) -> VmResult { + let Some(inner) = self.inner.checked_add(rhs.inner) else { + vm_panic!("overflow when adding duration to instant") + }; + + VmResult::Ok(Self { inner }) + } + + /// Add a duration to this instant and return a new instant. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::partial_eq; + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first.clone(); + /// second += Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = ADD_ASSIGN)] + #[inline] + fn add_assign(&mut self, rhs: &Duration) -> VmResult<()> { + let Some(inner) = self.inner.checked_add(rhs.inner) else { + vm_panic!("overflow when adding duration to instant") + }; + + self.inner = inner; + VmResult::Ok(()) + } + + /// Test two instants for partial equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::partial_eq; + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert_eq!(partial_eq(first, first), true); + /// assert_eq!(partial_eq(first, second), false); + /// assert_eq!(partial_eq(second, first), false); + /// ``` + #[rune::function(keep, instance, protocol = PARTIAL_EQ)] + #[inline] + fn partial_eq(&self, rhs: &Self) -> bool { + PartialEq::eq(&self.inner, &rhs.inner) + } + + /// Test two instants for total equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert_eq!(eq(first, first), true); + /// assert_eq!(eq(first, second), false); + /// assert_eq!(eq(second, first), false); + /// ``` + #[rune::function(keep, instance, protocol = EQ)] + #[inline] + fn eq(&self, rhs: &Self) -> bool { + PartialEq::eq(&self.inner, &rhs.inner) + } + + /// Perform a partial ordered comparison between two instants. + /// + /// # Examples + /// + /// ```rune + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert!(first < second); + /// assert!(second > first); + /// assert!(first == first); + /// ``` + /// + /// Using explicit functions: + /// + /// ```rune + /// use std::cmp::Ordering; + /// use std::ops::partial_cmp; + /// + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert_eq!(partial_cmp(first, second), Some(Ordering::Less)); + /// assert_eq!(partial_cmp(second, first), Some(Ordering::Greater)); + /// assert_eq!(partial_cmp(first, first), Some(Ordering::Equal)); + /// ``` + #[rune::function(keep, instance, protocol = PARTIAL_CMP)] + #[inline] + fn partial_cmp(&self, rhs: &Self) -> Option { + PartialOrd::partial_cmp(&self.inner, &rhs.inner) + } + + /// Perform a totally ordered comparison between two instants. + /// + /// # Examples + /// + /// ```rune + /// use std::cmp::Ordering; + /// use std::ops::cmp; + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first + Duration::SECOND; + /// + /// assert_eq!(cmp(first, second), Ordering::Less); + /// assert_eq!(cmp(second, first), Ordering::Greater); + /// assert_eq!(cmp(first, first), Ordering::Equal); + /// ``` + #[rune::function(keep, instance, protocol = CMP)] + #[inline] + fn cmp(&self, rhs: &Self) -> Ordering { + Ord::cmp(&self.inner, &rhs.inner) + } + + /// Hash the instant. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::hash; + /// use time::{Duration, Instant}; + /// + /// let now = Instant::now(); + /// + /// assert_eq!(hash(now), hash(now)); + /// ``` + #[rune::function(keep, instance, protocol = HASH)] + fn hash(&self, hasher: &mut Hasher) { + self.inner.hash(hasher); + } + + /// Write a debug representation of the instant. + /// + /// # Examples + /// + /// ```rune + /// use time::Instant; + /// + /// let now = Instant::now(); + /// + /// println!("{now:?}"); + /// ``` + #[rune::function(keep, instance, protocol = STRING_DEBUG)] + fn string_debug(&self, f: &mut Formatter) -> VmResult<()> { + rune::vm_write!(f, "{:?}", self.inner) + } + + /// Clone the current instant. + /// + /// # Examples + /// + /// ```rune + /// use time::{Duration, Instant}; + /// + /// let first = Instant::now(); + /// let second = first.clone(); + /// second += Duration::SECOND; + /// + /// assert!(first < second); + /// ``` + #[rune::function(keep, instance, protocol = CLONE)] + fn clone(&self) -> Self { + Self { inner: self.inner } + } }