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;
pub fn module(_stdio: bool) -> Result<Module, ContextError> {
let mut m = Module::with_crate("time")?;
m.function_meta(sleep)?;
m.function_meta(interval)?;
m.function_meta(interval_at)?;
m.ty::<Duration>()?;
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::<Duration>(item!(::std::cmp::PartialEq))?;
m.function_meta(Duration::eq__meta)?;
m.implement_trait::<Duration>(item!(::std::cmp::Eq))?;
m.function_meta(Duration::partial_cmp__meta)?;
m.implement_trait::<Duration>(item!(::std::cmp::PartialOrd))?;
m.function_meta(Duration::cmp__meta)?;
m.implement_trait::<Duration>(item!(::std::cmp::Ord))?;
m.function_meta(Duration::hash__meta)?;
m.function_meta(Duration::debug_fmt__meta)?;
m.function_meta(Duration::clone__meta)?;
m.implement_trait::<Duration>(item!(::std::clone::Clone))?;
m.constant(
"SECOND",
Duration {
inner: tokio::time::Duration::from_secs(1),
},
)
.build_associated::<Duration>()?
.docs(docstring! {
})?;
m.constant(
"MILLISECOND",
Duration {
inner: tokio::time::Duration::from_millis(1),
},
)
.build_associated::<Duration>()?
.docs(docstring! {
})?;
m.constant(
"MICROSECOND",
Duration {
inner: tokio::time::Duration::from_micros(1),
},
)
.build_associated::<Duration>()?
.docs(docstring! {
})?;
m.constant(
"NANOSECOND",
Duration {
inner: tokio::time::Duration::from_nanos(1),
},
)
.build_associated::<Duration>()?
.docs(docstring! {
})?;
m.constant(
"ZERO",
Duration {
inner: tokio::time::Duration::ZERO,
},
)
.build_associated::<Duration>()?
.docs(docstring! {
})?;
m.constant(
"MAX",
Duration {
inner: tokio::time::Duration::MAX,
},
)
.build_associated::<Duration>()?
.docs(docstring! {
})?;
m.ty::<Instant>()?;
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::<Instant>(item!(::std::cmp::PartialEq))?;
m.function_meta(Instant::eq__meta)?;
m.implement_trait::<Instant>(item!(::std::cmp::Eq))?;
m.function_meta(Instant::partial_cmp__meta)?;
m.implement_trait::<Instant>(item!(::std::cmp::PartialOrd))?;
m.function_meta(Instant::cmp__meta)?;
m.implement_trait::<Instant>(item!(::std::cmp::Ord))?;
m.function_meta(Instant::hash__meta)?;
m.function_meta(Instant::debug_fmt__meta)?;
m.function_meta(Instant::clone__meta)?;
m.implement_trait::<Instant>(item!(::std::clone::Clone))?;
m.ty::<Interval>()?;
m.function("tick", Interval::tick)
.build_associated::<Interval>()?;
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)
}
#[rune::function]
async fn sleep(duration: Duration) {
tokio::time::sleep(duration.inner).await;
}
#[rune::function]
async fn interval(period: Duration) -> Interval {
Interval {
inner: tokio::time::interval(period.inner),
}
}
#[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, ToConstValue)]
#[rune(item = ::time)]
pub struct Duration {
#[const_value(with = self::const_duration)]
inner: tokio::time::Duration,
}
impl Duration {
pub fn into_std(self) -> std::time::Duration {
std::time::Duration::new(self.inner.as_secs(), self.inner.subsec_nanos())
}
pub fn from_std(duration: std::time::Duration) -> Self {
Self {
inner: tokio::time::Duration::new(duration.as_secs(), duration.subsec_nanos()),
}
}
pub fn into_tokio(self) -> tokio::time::Duration {
self.inner
}
pub fn from_tokio(duration: tokio::time::Duration) -> Self {
Self { inner: duration }
}
#[rune::function(keep, path = Self::new)]
pub fn new(secs: u64, nanos: u32) -> VmResult<Self> {
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),
})
}
#[rune::function(keep, path = Self::from_secs)]
pub const fn from_secs(secs: u64) -> Self {
Self {
inner: tokio::time::Duration::from_secs(secs),
}
}
#[rune::function(keep, path = Self::from_millis)]
pub const fn from_millis(millis: u64) -> Self {
Self {
inner: tokio::time::Duration::from_millis(millis),
}
}
#[rune::function(keep, path = Self::from_micros)]
#[inline]
pub const fn from_micros(micros: u64) -> Self {
Self {
inner: tokio::time::Duration::from_micros(micros),
}
}
#[rune::function(keep, path = Self::from_nanos)]
#[inline]
pub const fn from_nanos(nanos: u64) -> Self {
Self {
inner: tokio::time::Duration::from_nanos(nanos),
}
}
#[rune::function(keep)]
#[inline]
pub const fn is_zero(&self) -> bool {
self.inner.is_zero()
}
#[rune::function(keep)]
#[inline]
pub const fn as_secs(&self) -> u64 {
self.inner.as_secs()
}
#[rune::function(keep)]
#[inline]
pub const fn subsec_millis(&self) -> u32 {
self.inner.subsec_millis()
}
#[rune::function(keep)]
#[inline]
pub const fn subsec_micros(&self) -> u32 {
self.inner.subsec_micros()
}
#[rune::function(keep)]
#[inline]
pub const fn subsec_nanos(&self) -> u32 {
self.inner.subsec_nanos()
}
#[rune::function(keep)]
#[inline]
pub const fn as_millis(&self) -> u128 {
self.inner.as_millis()
}
#[rune::function(keep)]
#[inline]
pub const fn as_micros(&self) -> u128 {
self.inner.as_micros()
}
#[rune::function(keep)]
#[inline]
pub const fn as_nanos(&self) -> u128 {
self.inner.as_nanos()
}
#[rune::function(keep)]
#[inline]
pub fn as_secs_f64(&self) -> f64 {
self.inner.as_secs_f64()
}
#[rune::function(keep, path = Self::from_secs_f64)]
pub fn from_secs_f64(secs: f64) -> VmResult<Self> {
match tokio::time::Duration::try_from_secs_f64(secs) {
Ok(duration) => VmResult::Ok(Self { inner: duration }),
Err(e) => vm_panic!(e),
}
}
#[rune::function(keep, instance, protocol = ADD)]
#[inline]
fn add(&self, rhs: &Duration) -> VmResult<Self> {
let Some(inner) = self.inner.checked_add(rhs.inner) else {
vm_panic!("overflow when adding durations")
};
VmResult::Ok(Self { inner })
}
#[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(())
}
#[rune::function(keep, instance, protocol = PARTIAL_EQ)]
#[inline]
fn partial_eq(&self, rhs: &Self) -> bool {
PartialEq::eq(&self.inner, &rhs.inner)
}
#[rune::function(keep, instance, protocol = EQ)]
#[inline]
fn eq(&self, rhs: &Self) -> bool {
PartialEq::eq(&self.inner, &rhs.inner)
}
#[rune::function(keep, instance, protocol = PARTIAL_CMP)]
#[inline]
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
PartialOrd::partial_cmp(&self.inner, &rhs.inner)
}
#[rune::function(keep, instance, protocol = CMP)]
#[inline]
fn cmp(&self, rhs: &Self) -> Ordering {
Ord::cmp(&self.inner, &rhs.inner)
}
#[rune::function(keep, instance, protocol = HASH)]
fn hash(&self, hasher: &mut Hasher) {
self.inner.hash(hasher);
}
#[rune::function(keep, instance, protocol = DEBUG_FMT)]
fn debug_fmt(&self, f: &mut Formatter) -> VmResult<()> {
rune::vm_write!(f, "{:?}", self.inner)
}
#[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<ConstValue, RuntimeError> {
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<Duration, RuntimeError> {
let (secs, nanos) = rune::from_const_value::<(u64, u32)>(value)?;
Ok(Duration::new(secs, nanos))
}
#[inline]
pub(super) fn from_value(value: Value) -> Result<Duration, RuntimeError> {
let (secs, nanos) = rune::from_value::<(u64, u32)>(value)?;
Ok(Duration::new(secs, nanos))
}
}
#[derive(Debug, Any)]
#[rune(item = ::time)]
pub struct Interval {
inner: tokio::time::Interval,
}
impl Interval {
pub async fn tick(mut internal: Mut<Interval>) {
internal.inner.tick().await;
}
#[rune::function(instance, keep)]
fn reset(&mut self) {
self.inner.reset();
}
#[rune::function(instance, keep)]
fn reset_immediately(&mut self) {
self.inner.reset_immediately();
}
#[rune::function(instance, keep)]
fn reset_after(&mut self, after: Duration) {
self.inner.reset_after(after.inner);
}
#[rune::function(instance, keep)]
fn reset_at(&mut self, deadline: Instant) {
self.inner.reset_at(deadline.inner);
}
}
#[derive(Debug, Any)]
#[rune(item = ::time)]
pub struct Instant {
inner: tokio::time::Instant,
}
impl Instant {
#[rune::function(keep, path = Self::now)]
pub fn now() -> Instant {
Instant {
inner: tokio::time::Instant::now(),
}
}
#[rune::function(instance, keep)]
pub fn duration_since(&self, earlier: Instant) -> Duration {
Duration {
inner: tokio::time::Instant::duration_since(&self.inner, earlier.inner),
}
}
#[rune::function(instance, keep)]
pub fn elapsed(&self) -> Duration {
Duration {
inner: tokio::time::Instant::elapsed(&self.inner),
}
}
#[rune::function(keep, instance, protocol = ADD)]
#[inline]
fn add(&self, rhs: &Duration) -> VmResult<Self> {
let Some(inner) = self.inner.checked_add(rhs.inner) else {
vm_panic!("overflow when adding duration to instant")
};
VmResult::Ok(Self { inner })
}
#[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(())
}
#[rune::function(keep, instance, protocol = PARTIAL_EQ)]
#[inline]
fn partial_eq(&self, rhs: &Self) -> bool {
PartialEq::eq(&self.inner, &rhs.inner)
}
#[rune::function(keep, instance, protocol = EQ)]
#[inline]
fn eq(&self, rhs: &Self) -> bool {
PartialEq::eq(&self.inner, &rhs.inner)
}
#[rune::function(keep, instance, protocol = PARTIAL_CMP)]
#[inline]
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
PartialOrd::partial_cmp(&self.inner, &rhs.inner)
}
#[rune::function(keep, instance, protocol = CMP)]
#[inline]
fn cmp(&self, rhs: &Self) -> Ordering {
Ord::cmp(&self.inner, &rhs.inner)
}
#[rune::function(keep, instance, protocol = HASH)]
fn hash(&self, hasher: &mut Hasher) {
self.inner.hash(hasher);
}
#[rune::function(keep, instance, protocol = DEBUG_FMT)]
fn debug_fmt(&self, f: &mut Formatter) -> VmResult<()> {
rune::vm_write!(f, "{:?}", self.inner)
}
#[rune::function(keep, instance, protocol = CLONE)]
fn clone(&self) -> Self {
Self { inner: self.inner }
}
}