From 6aa05a6c8bf07cd0b50c0dd203bb3e16eae91d86 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Tue, 29 Oct 2024 23:01:48 +0100 Subject: [PATCH] modules: Expand and add more impls in time module --- crates/rune-modules/src/time.rs | 984 ++++++++++++++++++++++++++------ 1 file changed, 814 insertions(+), 170 deletions(-) diff --git a/crates/rune-modules/src/time.rs b/crates/rune-modules/src/time.rs index 65894036f..99ff2f632 100644 --- a/crates/rune-modules/src/time.rs +++ b/crates/rune-modules/src/time.rs @@ -29,139 +29,198 @@ //! } //! ``` -use rune::{ - docstring, - runtime::{Mut, VmResult}, - vm_panic, 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.ty::()?; - module.ty::()?; - - module.function_meta(Duration::new__meta)?; - module.function_meta(Duration::from_secs__meta)?; - module.function_meta(Duration::from_millis__meta)?; - module.function_meta(Duration::from_micros__meta)?; - module.function_meta(Duration::from_nanos__meta)?; - module.function_meta(Duration::as_secs_f64__meta)?; - module.function_meta(Duration::from_secs_f64__meta)?; - - /* TODO: Make Duration a ConstValue - module - .constant("SECOND", Duration::from_secs(1)) - .build_associated::()? - .docs(docstring! { - /// The duration of one second. - /// - /// # Examples - /// - /// ```rune,no_run - /// use time::Duration; - /// - /// let duration = Duration::SECOND; - /// ``` - })?; - - module - .constant("MILLISECOND", Duration::from_millis(1)) - .build_associated::()? - .docs(docstring! { - /// The duration of one millisecond. - /// - /// # Examples - /// - /// ```rune,no_run - /// use time::Duration; - /// - /// let duration = Duration::MILLISECOND; - /// ``` - })?; - - module - .constant("MICROSECOND", Duration::from_micros(1)) - .build_associated::()? - .docs(docstring! { - /// The duration of one microsecond. - /// - /// # Examples - /// - /// ```rune,no_run - /// use time::Duration; - /// - /// let duration = Duration::MICROSECOND; - /// ``` - })?; - - module - .constant("NANOSECOND", Duration::from_nanos(1)) - .build_associated::()? - .docs(docstring! { - /// The duration of one nanosecond. - /// - /// # Examples - /// - /// ```rune,no_run - /// use time::Duration; - /// - /// let duration = Duration::NANOSECOND; - /// ``` - })?; - - module - .constant("ZERO", Duration::from_nanos(0)) - .build_associated::()? - .docs(docstring! { - /// A duration of zero time. - /// - /// # Examples - /// - /// ```rune,no_run - /// use time::Duration; - /// - /// let duration = Duration::ZERO; - /// ``` - })?; - - module - .constant("MAX", Duration::new(u64::MAX, NANOS_PER_SEC - 1)) - .build_associated::()? - .docs(docstring! { - /// The maximum duration. - /// - /// # Examples - /// - /// ```rune,no_run - /// use time::Duration; - /// - /// let duration = Duration::MAX; - /// ``` - })?; - */ - - module - .function("tick", Interval::tick) - .build_associated::()?; - module.function_meta(Interval::reset__meta)?; - module.function_meta(Interval::reset_immediately__meta)?; - module.function_meta(Interval::reset_after__meta)?; - module.function_meta(Interval::reset_at__meta)?; + let mut m = Module::with_crate("time")?; + + m.function_meta(sleep)?; + m.function_meta(interval)?; + m.function_meta(interval_at)?; - module.function_meta(Instant::now__meta)?; - module.function_meta(Instant::duration_since__meta)?; - module.function_meta(Instant::elapsed__meta)?; + 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; + /// ``` + })?; - module.function_meta(sleep)?; - module.function_meta(interval)?; - module.function_meta(interval_at)?; + 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; + /// ``` + })?; - Ok(module) + 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. @@ -172,7 +231,7 @@ pub fn module(_stdio: bool) -> Result { /// use time::Duration; /// /// let duration = Duration::from_secs(10); -/// time::sleep(d).await; +/// time::sleep(duration).await; /// println!("Surprise!"); /// ``` #[rune::function] @@ -248,7 +307,7 @@ async fn interval_at(start: Instant, period: Duration) -> Interval { /// /// # Examples /// -/// ```rune,no_run +/// ```rune /// use time::Duration; /// /// let five_seconds = Duration::new(5, 0); @@ -259,13 +318,54 @@ async fn interval_at(start: Instant, period: Duration) -> Interval { /// /// let ten_millis = Duration::from_millis(10); /// ``` -#[derive(Debug, Clone, Copy, Any)] +#[derive(Debug, Clone, Copy, Any, ToConstValue)] #[rune(item = ::time)] pub struct Duration { + #[const_value(with = self::const_duration)] inner: tokio::time::Duration, } impl Duration { + /// 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. /// @@ -279,17 +379,15 @@ impl Duration { /// /// # Examples /// - /// ```rune,no_run + /// ```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 { - if secs.checked_add((nanos / NANOS_PER_SEC) as u64).is_none() { - vm_panic!("overflow in Duration::new"); - } + 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 { @@ -301,7 +399,7 @@ impl Duration { /// /// # Examples /// - /// ```rune,no_run + /// ```rune /// use time::Duration; /// /// let duration = Duration::from_secs(5); @@ -317,7 +415,7 @@ impl Duration { /// /// # Examples /// - /// ```rune,no_run + /// ```rune /// use time::Duration; /// /// let duration = Duration::from_millis(2569); @@ -333,12 +431,13 @@ impl Duration { /// /// # Examples /// - /// ```rune,no_run + /// ```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), @@ -354,31 +453,195 @@ impl Duration { /// /// # Examples /// - /// ```rune,no_run + /// ```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. + /// The returned value does include the fractional (nanosecond) part of the + /// duration. /// /// # Examples /// - /// ```rune,no_run + /// ```rune /// use time::Duration; /// /// let duration = Duration::from_secs(60).as_secs_f64(); /// ``` - #[rune::function(keep, path = Self::as_secs_f64)] - pub const fn as_secs_f64(&self) -> f64 { + #[rune::function(keep)] + #[inline] + pub fn as_secs_f64(&self) -> f64 { self.inner.as_secs_f64() } @@ -387,7 +650,7 @@ impl Duration { /// /// # Examples /// - /// ```rune,no_run + /// ```rune /// use time::Duration; /// /// let duration = Duration::from_secs_f64(0.0); @@ -399,47 +662,230 @@ impl Duration { Err(e) => vm_panic!(e), } } -} -impl Duration { - /// 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()) + /// 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 }) } - /// 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()), - } + /// 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(()) } - /// Converts [`Duration`] into a [`tokio::time::Duration`]. + /// Test two durations for partial equality. /// - /// # Example + /// # 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 duration = Duration::from_secs(5); - /// let tokio_duration = duration.into_tokio(); + /// 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); /// ``` - pub fn into_tokio(self) -> tokio::time::Duration { - self.inner + #[rune::function(keep, instance, protocol = EQ)] + #[inline] + fn eq(&self, rhs: &Self) -> bool { + PartialEq::eq(&self.inner, &rhs.inner) } - /// Creates a [`Duration`] from a [`tokio::time::Duration`]. + /// Perform a partial ordered comparison between two durations. /// - /// # Example + /// # 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 tokio_duration = tokio::time::Duration::from_secs(5); - /// let duration = Duration::from_tokio(tokio_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); /// ``` - pub fn from_tokio(duration: tokio::time::Duration) -> Self { - Self { inner: duration } + #[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)) } } @@ -612,7 +1058,7 @@ impl Instant { /// /// # Examples /// - /// ```rune,no_run + /// ```rune /// use time::{Duration, Instant}; /// /// let instant = Instant::now(); @@ -630,12 +1076,12 @@ impl Instant { /// # Examples /// /// ```rune,no_run - /// use time::{Instant, Duration}; + /// use time::{Duration, Instant}; /// /// let instant = Instant::now(); /// /// let three_secs = Duration::from_secs(3); - /// sleep(three_secs).await; + /// time::sleep(three_secs).await; /// /// let now = Instant::now(); /// let duration_since = now.duration_since(instant); @@ -647,8 +1093,8 @@ impl Instant { } } - /// Returns the amount of time elapsed since this instant was created, - /// or zero duration if that this instant is in the future. + /// Returns the amount of time elapsed since this instant was created, or + /// zero duration if that this instant is in the future. /// /// # Examples /// @@ -658,7 +1104,7 @@ impl Instant { /// let instant = Instant::now(); /// /// let three_secs = Duration::from_secs(3); - /// sleep(three_secs).await; + /// time::sleep(three_secs).await; /// /// let elapsed = instant.elapsed(); /// ``` @@ -668,4 +1114,202 @@ impl Instant { 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 } + } }