use cap_std::time::{Duration, Instant, SystemClock, SystemTime};
use cap_std::{AmbientAuthority, ambient_authority};
use std::error::Error;
use std::fmt;
use wasmtime::component::{HasData, ResourceTable};
pub struct WasiClocks;
impl HasData for WasiClocks {
type Data<'a> = WasiClocksCtxView<'a>;
}
pub struct WasiClocksCtx {
pub(crate) wall_clock: Box<dyn HostWallClock + Send>,
pub(crate) monotonic_clock: Box<dyn HostMonotonicClock + Send>,
}
impl Default for WasiClocksCtx {
fn default() -> Self {
Self {
wall_clock: wall_clock(),
monotonic_clock: monotonic_clock(),
}
}
}
pub trait WasiClocksView: Send {
fn clocks(&mut self) -> WasiClocksCtxView<'_>;
}
pub struct WasiClocksCtxView<'a> {
pub ctx: &'a mut WasiClocksCtx,
pub table: &'a mut ResourceTable,
}
pub trait HostWallClock: Send {
fn resolution(&self) -> Duration;
fn now(&self) -> Duration;
}
pub trait HostMonotonicClock: Send {
fn resolution(&self) -> u64;
fn now(&self) -> u64;
}
pub struct WallClock {
clock: cap_std::time::SystemClock,
}
impl Default for WallClock {
fn default() -> Self {
Self::new(ambient_authority())
}
}
impl WallClock {
pub fn new(ambient_authority: AmbientAuthority) -> Self {
Self {
clock: cap_std::time::SystemClock::new(ambient_authority),
}
}
}
impl HostWallClock for WallClock {
fn resolution(&self) -> Duration {
#[cfg(unix)]
{
let res = rustix::time::clock_getres(rustix::time::ClockId::Realtime);
Duration::new(
res.tv_sec.try_into().unwrap(),
res.tv_nsec.try_into().unwrap(),
)
}
#[cfg(windows)]
{
Duration::new(0, 55_000_000)
}
}
fn now(&self) -> Duration {
self.clock
.now()
.duration_since(SystemClock::UNIX_EPOCH)
.unwrap()
}
}
pub struct MonotonicClock {
clock: cap_std::time::MonotonicClock,
initial: Instant,
}
impl Default for MonotonicClock {
fn default() -> Self {
Self::new(ambient_authority())
}
}
impl MonotonicClock {
pub fn new(ambient_authority: AmbientAuthority) -> Self {
let clock = cap_std::time::MonotonicClock::new(ambient_authority);
let initial = clock.now();
Self { clock, initial }
}
}
impl HostMonotonicClock for MonotonicClock {
fn resolution(&self) -> u64 {
#[cfg(unix)]
{
let res = rustix::time::clock_getres(rustix::time::ClockId::Monotonic);
u64::try_from(res.tv_sec).unwrap() * 1_000_000_000 + u64::try_from(res.tv_nsec).unwrap()
}
#[cfg(windows)]
{
use windows_sys::Win32::System::Performance::QueryPerformanceFrequency;
unsafe {
let mut frequency = 0;
if QueryPerformanceFrequency(&mut frequency) == 0 {
panic!(
"QueryPerformanceFrequency failed: {}",
std::io::Error::last_os_error()
);
}
1_000_000_000 / u64::try_from(frequency).unwrap()
}
}
}
fn now(&self) -> u64 {
self.clock
.now()
.duration_since(self.initial)
.as_nanos()
.try_into()
.unwrap()
}
}
pub fn monotonic_clock() -> Box<dyn HostMonotonicClock + Send> {
Box::new(MonotonicClock::default())
}
pub fn wall_clock() -> Box<dyn HostWallClock + Send> {
Box::new(WallClock::default())
}
pub(crate) struct Datetime {
pub seconds: i64,
pub nanoseconds: u32,
}
impl TryFrom<SystemTime> for Datetime {
type Error = DatetimeError;
fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
let epoch = SystemTime::from_std(std::time::SystemTime::UNIX_EPOCH);
if time >= epoch {
let duration = time.duration_since(epoch)?;
Ok(Self {
seconds: duration.as_secs().try_into()?,
nanoseconds: duration.subsec_nanos(),
})
} else {
let duration = epoch.duration_since(time)?;
Ok(Self {
seconds: -duration.as_secs().try_into()?,
nanoseconds: duration.subsec_nanos(),
})
}
}
}
#[derive(Debug)]
pub struct DatetimeError;
impl fmt::Display for DatetimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("couldn't represent time as a WASI `Datetime`")
}
}
impl Error for DatetimeError {}
impl From<std::time::SystemTimeError> for DatetimeError {
fn from(_: std::time::SystemTimeError) -> Self {
DatetimeError
}
}
impl From<std::num::TryFromIntError> for DatetimeError {
fn from(_: std::num::TryFromIntError) -> Self {
DatetimeError
}
}