Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rust): Time Module Implementation #142

Merged
merged 29 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e389df9
build(deps): add once_cell and tokio crates
saibatizoku Mar 7, 2024
c1c1738
fix: cleanup lint in cron
saibatizoku Mar 7, 2024
a399dbe
feat: implement monotonic clock api
saibatizoku Mar 7, 2024
a6bac59
feat: implement wall clock api
saibatizoku Mar 7, 2024
355f5a5
deps: use features from time crate
saibatizoku Feb 17, 2024
c9c1876
feat(wip): stub code for localtime module
saibatizoku Feb 19, 2024
51af95d
feat(wip): initial implementation code for get_localtime and alt_loca…
saibatizoku Feb 21, 2024
64d4d84
feat: implement time module
saibatizoku Feb 21, 2024
ffd8b02
feat(wip): monotonic clock resolution
saibatizoku Feb 22, 2024
616b5ee
fix: remove extra info
saibatizoku Feb 22, 2024
1710af7
chore: update project dictionary
saibatizoku Feb 22, 2024
184d96f
fix: merge deps
saibatizoku Mar 6, 2024
72a07ec
Merge branch 'feat/clocks-api' into feat/time-api-rust
saibatizoku Mar 8, 2024
50e585a
build(deps): remove time-tz and libc, add chrono-tz
saibatizoku Mar 9, 2024
ad854e1
feat: monotonic clock state implemented
saibatizoku Mar 9, 2024
98c835f
fix: update localtime api to use chrono-tz
saibatizoku Mar 9, 2024
97cdcc5
build(deps): add iana-time-zone crate
saibatizoku Mar 11, 2024
37a075d
feat: cleanup localtime API
saibatizoku Mar 11, 2024
be40d77
chore: update project dictionary
saibatizoku Mar 11, 2024
7908939
Merge remote-tracking branch 'origin/main' into feat/time-api-rust
saibatizoku Mar 11, 2024
95c1388
deps: remove unused features from time crate
saibatizoku Mar 11, 2024
f690c7e
fix: error for invalid localtime
saibatizoku Mar 11, 2024
f6d945b
fix: requested changes to monotonic clock state
saibatizoku Mar 12, 2024
af9fbf5
fix: remove debug code
saibatizoku Mar 12, 2024
a276596
fix: Apply suggestions from code review
saibatizoku Mar 12, 2024
feac53a
fix: update docstring for base instant
saibatizoku Mar 12, 2024
c0a00cd
Merge branch 'main' into feat/time-api-rust
saibatizoku Mar 12, 2024
6766b84
feat: clock api state contains resolution
saibatizoku Mar 13, 2024
d04217b
Merge remote-tracking branch 'origin/main' into feat/time-api-rust
saibatizoku Mar 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ codepoints
coti
crontabs
cryptoxide
Datelike
dbsync
dcbor
delegators
Expand Down Expand Up @@ -48,6 +49,7 @@ fstat
fstatat
futimens
genhtml
getres
GETFL
gmtime
happ
Expand All @@ -65,6 +67,7 @@ jormungandr
Jörmungandr
lcov
Leshiy
libc
linkat
lintfix
localizable
Expand All @@ -81,6 +84,7 @@ multiera
nanos
netkey
nextest
nsec
oneshot
openapi
openat
Expand Down Expand Up @@ -119,13 +123,16 @@ seckey
slotno
smac
stevenj
subsec
symlinkat
syscall
tacho
testcov
testdocs
testunit
thiserror
timelike
timespec
toolsets
Traceback
txns
Expand Down
3 changes: 3 additions & 0 deletions hermes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,7 @@ tracing = "0.1.40"
tracing-subscriber = "0.3.18"
criterion = "0.5.1"
time = "0.3.34"
chrono = "0.4.34"
chrono-tz = "0.8.6"
once_cell = "1.19.0"
iana-time-zone = "0.1.60"
3 changes: 3 additions & 0 deletions hermes/bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ hex-literal = { workspace = true }
thiserror = { workspace = true }
criterion = { workspace = true, optional = true }
time = { workspace = true }
chrono = { workspace = true }
chrono-tz = { workspace = true }
once_cell = { workspace = true }
iana-time-zone = { workspace = true }
2 changes: 0 additions & 2 deletions hermes/bin/src/runtime_extensions/hermes/cron/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,6 @@ impl Ord for CronComponent {

#[cfg(test)]
mod tests {
use time::Duration;

use super::*;

// Define lower limit for the schedule component values.
Expand Down
46 changes: 37 additions & 9 deletions hermes/bin/src/runtime_extensions/hermes/localtime/host.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
//! Localtime host implementation for WASM runtime.

use chrono::{Local, TimeZone};
use chrono_tz::Tz;

use crate::{
runtime_context::HermesRuntimeContext,
runtime_extensions::bindings::{
hermes::localtime::api::{Errno, Host, Localtime, Timezone},
wasi::clocks::wall_clock::Datetime,
runtime_extensions::{
bindings::{
hermes::localtime::api::{Errno, Host, Localtime, Timezone},
wasi::clocks::wall_clock::Datetime,
},
hermes::localtime::get_tz,
},
};

Expand All @@ -23,9 +29,23 @@ impl Host for HermesRuntimeContext {
/// `localtime` : the converted time.
/// `errno` : An error indicating why conversion failed.
fn get_localtime(
&mut self, _when: Option<Datetime>, _tz: Option<Timezone>,
&mut self, when: Option<Datetime>, tz: Option<Timezone>,
) -> wasmtime::Result<Result<Localtime, Errno>> {
todo!()
let timezone = get_tz(tz)?;
let local_naive = match when {
Some(Datetime {
seconds,
nanoseconds,
}) => {
let seconds = seconds.try_into().map_err(|_| Errno::InvalidLocaltime)?;
let utc_dt = chrono::DateTime::from_timestamp(seconds, nanoseconds)
.ok_or(Errno::InvalidLocaltime)?;
utc_dt.naive_utc()
},
None => Local::now().naive_utc(),
};
let local_date_time = timezone.from_utc_datetime(&local_naive);
Ok(local_date_time.try_into())
}

/// Get a new localtime from a localtime, by recalculating time for a new timezone.
Expand All @@ -41,9 +61,17 @@ impl Host for HermesRuntimeContext {
/// `localtime` : the converted time.
/// `errno` : An error indicating why conversion failed.
fn alt_localtime(
&mut self, _time: Localtime, _tz: Option<Timezone>,
&mut self, time: Localtime, tz: Option<Timezone>,
) -> wasmtime::Result<Result<Localtime, Errno>> {
todo!()
let local_date_time: chrono::DateTime<Tz> = time.try_into()?;
let alt_local_date_time = match tz {
Some(alt_tz) => {
let tz = get_tz(Some(alt_tz))?;
tz.from_utc_datetime(&local_date_time.naive_utc())
},
None => local_date_time,
};
Ok(alt_local_date_time.try_into())
}

/// Get a datetime from a localtime.
Expand All @@ -56,7 +84,7 @@ impl Host for HermesRuntimeContext {
///
/// `datetime` : the converted time.
/// `errno` : An error indicating why conversion failed.
fn get_datetime(&mut self, _time: Localtime) -> wasmtime::Result<Result<Datetime, Errno>> {
todo!()
fn get_datetime(&mut self, time: Localtime) -> wasmtime::Result<Result<Datetime, Errno>> {
Ok(time.try_into())
}
}
114 changes: 114 additions & 0 deletions hermes/bin/src/runtime_extensions/hermes/localtime/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,120 @@
//! Localtime runtime extension implementation.

use chrono::{Datelike, NaiveDate, TimeZone, Timelike};
use chrono_tz::{OffsetName, Tz};

use crate::runtime_extensions::bindings::{
hermes::localtime::api::{Errno, Localtime, Timezone},
wasi::clocks::wall_clock::Datetime,
};

mod host;

/// Advise Runtime Extensions of a new context
pub(crate) fn new_context(_ctx: &crate::runtime_context::HermesRuntimeContext) {}

/// Get `Tz` from an optional `Timezone`.
///
/// If present, `tz` is parsed into `Tz`, otherwise the system timezone is
/// returned.
///
/// This function returns `Errno::UnknownTimezone` if `tz` cannot be parsed.
///
/// **Parameters**
/// * `tz`: `Option<Timezone>`.
///
/// **Returns**
/// `wasmtime::Result<Result<Tz, Errno>>`.
pub(crate) fn get_tz(tz: Option<Timezone>) -> Result<Tz, Errno> {
let timezone_str = match tz {
Some(tz_str) => tz_str,
None => iana_time_zone::get_timezone().map_err(|_| Errno::UnknownTimezone)?,
};
timezone_str.parse().map_err(|_| Errno::UnknownTimezone)
}

impl TryFrom<chrono::DateTime<Tz>> for Localtime {
type Error = Errno;

fn try_from(dt: chrono::DateTime<Tz>) -> Result<Self, Self::Error> {
let localtime = Localtime {
year: dt.year().try_into().map_err(|_| Errno::YearOutOfRange)?,
month: dt.month().try_into().map_err(|_| Errno::InvalidLocaltime)?,
dow: dt
.weekday()
.number_from_monday()
.try_into()
.map_err(|_| Errno::InvalidLocaltime)?,
day: dt.day().try_into().map_err(|_| Errno::InvalidLocaltime)?,
hh: dt.hour().try_into().map_err(|_| Errno::InvalidLocaltime)?,
mm: dt
.minute()
.try_into()
.map_err(|_| Errno::InvalidLocaltime)?,
ss: dt
.second()
.try_into()
.map_err(|_| Errno::InvalidLocaltime)?,
ns: dt.nanosecond(),
tz: dt.offset().tz_id().to_string(),
};
Ok(localtime)
}
}

impl TryInto<chrono::DateTime<Tz>> for Localtime {
type Error = Errno;

fn try_into(self) -> Result<chrono::DateTime<Tz>, Self::Error> {
let Localtime {
year,
month,
dow: _,
day,
hh,
mm,
ss,
ns,
tz: orig_tz,
} = self;
let orig_tz = get_tz(Some(orig_tz))?;
let year: i32 = year.try_into().map_err(|_| Errno::YearOutOfRange)?;
let month: u32 = month.into();
let day: u32 = day.into();
let hh: u32 = hh.into();
let mm: u32 = mm.into();
let ss: u32 = ss.into();
let ns: u32 = ns;
let naive_date_time = NaiveDate::from_ymd_opt(year, month, day)
.ok_or(Errno::InvalidLocaltime)?
.and_hms_nano_opt(hh, mm, ss, ns)
.ok_or(Errno::InvalidLocaltime)?;
let date_time = orig_tz.from_utc_datetime(&naive_date_time);
Ok(date_time)
}
}

impl TryFrom<chrono::DateTime<Tz>> for Datetime {
type Error = Errno;

fn try_from(value: chrono::DateTime<Tz>) -> Result<Self, Self::Error> {
let seconds = value
.timestamp()
.try_into()
.map_err(|_| Errno::InvalidLocaltime)?;
let nanoseconds = value.nanosecond();
Ok(Datetime {
seconds,
nanoseconds,
})
}
}

impl TryFrom<Localtime> for Datetime {
type Error = Errno;

fn try_from(value: Localtime) -> Result<Self, Self::Error> {
let dt: chrono::DateTime<Tz> = value.try_into()?;
dt.try_into()
}
}
1 change: 1 addition & 0 deletions hermes/bin/src/runtime_extensions/wasi/clocks/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Host - WASI - Clock implementations

mod monotonic;
mod state;
mod wall;

/// Advise Runtime Extensions of a new context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

use crate::{
runtime_context::HermesRuntimeContext,
runtime_extensions::bindings::wasi::clocks::monotonic_clock::{Duration, Host, Instant},
runtime_extensions::{
bindings::wasi::clocks::monotonic_clock::{Duration, Host, Instant},
wasi::clocks::state::{monotonic_clock_now, monotonic_clock_res},
},
};

impl Host for HermesRuntimeContext {
Expand All @@ -11,12 +14,12 @@ impl Host for HermesRuntimeContext {
/// The clock is monotonic, therefore calling this function repeatedly will
/// produce a sequence of non-decreasing values.
fn now(&mut self) -> wasmtime::Result<Instant> {
todo!()
monotonic_clock_now()
}

/// Query the resolution of the clock. Returns the duration of time
/// corresponding to a clock tick.
fn resolution(&mut self) -> wasmtime::Result<Duration> {
todo!()
Ok(monotonic_clock_res())
}
}
Loading
Loading