Skip to content

Commit

Permalink
Restructure project to separate core provider APIs from non-provider …
Browse files Browse the repository at this point in the history
…APIs (#169)

This PR lays out the general concept laid out in #165.

Primarily, this PR splits the core Temporal functionality that can be
used by engines into the `builtins::core` module and then builds out
wrappers for nearly all of the core builtins (Currently only
`PlainYearMonth` and `PlainMonthDay` have not had wrappers implemented)
that can be used directly from Rust in the `builtins::native` module.

Ideally, this provides a better experience for using the library
natively from Rust (while also giving us a cleaner API to write tests
with), but still allows `with_provider` API to be used for engines
and/or anyone else who wants to implement their own `TimeZoneProvider`.
  • Loading branch information
nekevss authored Jan 22, 2025
1 parent 59bd045 commit f1154b0
Show file tree
Hide file tree
Showing 39 changed files with 2,510 additions and 935 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ combine = { workspace = true, optional = true }
web-time = { workspace = true, optional = true }

[features]
default = ["now"]
default = ["full"]
log = ["dep:log"]
experimental = ["tzdb"]
full = ["tzdb", "now"]
now = ["std", "dep:web-time"]
tzdb = ["dep:tzif", "std", "dep:jiff-tzdb", "dep:combine"]
std = []
112 changes: 112 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Library Architecture

TODO: FFI docs

This doc provides an overview of the layout of `temporal_rs`.

We will go over the Temporal Date/Time builtins, general primitives, and
utiltity crates.

## `temporal_rs` design considerations

`temporal_rs` is first and foremost designed to be a fully spec
compliant implementation of ECMAScript's Temporal date/time builtins.

As such, the main design consideration of `temporal_rs` is that it needs
to be able to service language interpreters / engines.

Thus, `temporal_rs` aims to provide an API along with tools to implement
Temporal while minimizing issue with integrating Temporal into engines.

## Date/Time builtins

The primary date & time builtins/components are located in the
`builtins` directory.

These builtins are then reexported from `lib.rs` to be available from
`temporal_rs`'s root module.

### Core vs. Native

`temporal_rs`'s builtins are split in two distinct directories `core`
and `native`. The core implementation contains the core implementation
of the Temporal builtins; meanwhile, the `native` implementation is a
Rust wrapper around the `core` implementation that simplifies some
"lower" level date/time API that may not be necessary for a general use
case.

### Core implementation

The core implementation is always publicly available, but may not be
available to import from the `temporal_rs`'s root.

The core implementation can be made available from the root by providing
the `--no-default-features` flag.

The core implementation exposes the Provider API that allows the user to
supply a "provider", or any type that implements the `TimeZoneProvider`
trait, for time zone data that the library can use to complete it's
calculations. This is useful from an engine / implementor perspective
because it allows the engine to source time zone data in their preferred
manner without locking them into a library specific implementation that
may or may not have side effects.

A `TimeZoneProvider` API on a core builtin will look like the below.

```rust
impl ZonedDateTime {
pub fn day_with_provider(&self, provider: &impl TimeZoneProvider) -> TemporalResult<u8> {
// Code goes here.
}
}
```

### Native implementation

The native implementation is only available via the "full" default
feature flag.

For the same reason that the Provider API is useful for language
implementors, it is a deterent from a general use case perspective. Most
people using a datetime library, outside of the self-proclaimed time
zone nerds, probably won't care from where their time zone data is being
sourced.

The native Rust wrapper of the core implementation provides a default
provider implementation to remove the need of the user to think or deal
with `TimeZoneProvider`.

```rust
impl ZonedDateTime {
pub fn day(&self) -> TemporalResult<u8> {
// Code goes here.
}
}
```

This greatly simplifies the API for general use cases.

## Primitives

<!-- Should IsoDate, IsoTime, and IsoDateTime be considered primitives? -->

`temporal_rs` has a primitive number implementation `FiniteF64` along
with a few date and time primitives: `IsoDate`, `IsoTime`,
`IsoDateTime`, and `EpochNanoseconds`.

`FiniteF64` allows an interface to translate between ECMAScript's number
type vs. `temporal_rs`'s strictly typed API.

Meanwhile the Date and Time primitives allow certain invariants to be
enforced on their records.

## Utiltiies

`temporal_rs` provides one implementation of the `TimeZoneProvider`
trait: `FsTzdbProvider`.

`FsTzdbProvider` reads from the file systems' tzdb and when not
available on the system, `FsTzdbProvider` relies on a prepackaged
`tzdb`.

<!-- TODO: add some more about parsers -->
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use core::str::FromStr;
use icu_calendar::types::{Era as IcuEra, MonthCode as IcuMonthCode, MonthInfo, YearInfo};

use crate::{
components::{
builtins::core::{
duration::{DateDuration, TimeDuration},
Duration, PlainDate, PlainDateTime, PlainMonthDay, PlainYearMonth,
},
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::iso::{constrain_iso_day, is_valid_iso_day};
use crate::options::ArithmeticOverflow;
use crate::{TemporalError, TemporalResult};

use crate::components::{calendar::Calendar, PartialDate};
use crate::builtins::core::{calendar::Calendar, PartialDate};

/// `ResolvedCalendarFields` represents the resolved field values necessary for
/// creating a Date from potentially partial values.
Expand Down Expand Up @@ -322,7 +322,7 @@ mod tests {
use tinystr::tinystr;

use crate::{
components::{calendar::Calendar, PartialDate},
builtins::core::{calendar::Calendar, PartialDate},
options::ArithmeticOverflow,
};

Expand Down
8 changes: 5 additions & 3 deletions src/components/date.rs → src/builtins/core/date.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
//! This module implements `Date` and any directly related algorithms.
use crate::{
components::{calendar::Calendar, duration::DateDuration, Duration, PlainDateTime},
builtins::core::{
calendar::Calendar, duration::DateDuration, Duration, PlainDateTime, PlainTime,
},
iso::{IsoDate, IsoDateTime, IsoTime},
options::{
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, DisplayCalendar,
ResolvedRoundingOptions, TemporalUnit,
},
parsers::{parse_date_time, IxdtfStringBuilder},
primitive::FiniteF64,
provider::NeverProvider,
TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
};
use alloc::{format, string::String};
Expand All @@ -17,8 +20,7 @@ use core::str::FromStr;
use super::{
calendar::{ascii_four_to_integer, month_to_month_code},
duration::{normalized::NormalizedDurationRecord, TimeDuration},
timezone::NeverProvider,
PlainMonthDay, PlainTime, PlainYearMonth,
PlainMonthDay, PlainYearMonth,
};
use tinystr::TinyAsciiStr;

Expand Down
15 changes: 7 additions & 8 deletions src/components/datetime.rs → src/builtins/core/datetime.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
//! This module implements `DateTime` any directly related algorithms.
use super::{
duration::normalized::{NormalizedDurationRecord, NormalizedTimeDuration},
Duration, PartialDate, PartialTime, PlainDate, PlainTime,
};
use crate::{
components::{calendar::Calendar, Instant},
builtins::core::{calendar::Calendar, Instant},
iso::{IsoDate, IsoDateTime, IsoTime},
options::{
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, DisplayCalendar,
ResolvedRoundingOptions, RoundingOptions, TemporalUnit, ToStringRoundingOptions,
},
parsers::{parse_date_time, IxdtfStringBuilder},
provider::NeverProvider,
temporal_assert, TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
};
use alloc::string::String;
use core::{cmp::Ordering, str::FromStr};

use super::{
duration::normalized::{NormalizedDurationRecord, NormalizedTimeDuration},
timezone::NeverProvider,
Duration, PartialDate, PartialTime, PlainDate, PlainTime,
};
use tinystr::TinyAsciiStr;

/// A partial PlainDateTime record
Expand Down Expand Up @@ -693,7 +692,7 @@ mod tests {
use tinystr::{tinystr, TinyAsciiStr};

use crate::{
components::{
builtins::core::{
calendar::Calendar, duration::DateDuration, Duration, PartialDate, PartialDateTime,
PartialTime, PlainDateTime,
},
Expand Down
30 changes: 6 additions & 24 deletions src/components/duration.rs → src/builtins/core/duration.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//! This module implements `Duration` along with it's methods and components.
use crate::{
components::{timezone::TimeZoneProvider, PlainDateTime, PlainTime},
builtins::core::{options::RelativeTo, PlainDateTime, PlainTime, ZonedDateTime},
iso::{IsoDateTime, IsoTime},
options::{
ArithmeticOverflow, RelativeTo, ResolvedRoundingOptions, RoundingIncrement,
RoundingOptions, TemporalUnit, ToStringRoundingOptions,
ArithmeticOverflow, ResolvedRoundingOptions, RoundingIncrement, RoundingOptions,
TemporalUnit, ToStringRoundingOptions,
},
parsers::{FormattableDuration, Precision},
primitive::FiniteF64,
provider::TimeZoneProvider,
temporal_assert, Sign, TemporalError, TemporalResult,
};
use alloc::format;
Expand All @@ -25,9 +26,6 @@ use num_traits::AsPrimitive;

use self::normalized::NormalizedTimeDuration;

#[cfg(feature = "experimental")]
use crate::components::timezone::TZ_PROVIDER;

mod date;
pub(crate) mod normalized;
mod time;
Expand All @@ -40,8 +38,6 @@ pub use date::DateDuration;
#[doc(inline)]
pub use time::TimeDuration;

use super::ZonedDateTime;

/// A `PartialDuration` is a Duration that may have fields not set.
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
pub struct PartialDuration {
Expand Down Expand Up @@ -91,7 +87,7 @@ impl core::fmt::Display for Duration {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(
&self
.to_temporal_string(ToStringRoundingOptions::default())
.as_temporal_string(ToStringRoundingOptions::default())
.expect("Duration must return a valid string with default options."),
)
}
Expand Down Expand Up @@ -639,7 +635,7 @@ impl Duration {
}
}

pub fn to_temporal_string(&self, options: ToStringRoundingOptions) -> TemporalResult<String> {
pub fn as_temporal_string(&self, options: ToStringRoundingOptions) -> TemporalResult<String> {
if options.smallest_unit == Some(TemporalUnit::Hour)
|| options.smallest_unit == Some(TemporalUnit::Minute)
{
Expand Down Expand Up @@ -732,20 +728,6 @@ pub fn duration_to_formattable(
})
}

#[cfg(feature = "experimental")]
impl Duration {
pub fn round(
&self,
options: RoundingOptions,
relative_to: Option<RelativeTo>,
) -> TemporalResult<Self> {
let provider = TZ_PROVIDER
.lock()
.map_err(|_| TemporalError::general("Unable to acquire lock"))?;
self.round_with_provider(options, relative_to, &*provider)
}
}

// TODO: Update, optimize, and fix the below. is_valid_duration should probably be generic over a T.

const TWO_POWER_FIFTY_THREE: i128 = 9_007_199_254_740_992;
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ use core::{num::NonZeroU128, ops::Add};
use num_traits::{AsPrimitive, Euclid, FromPrimitive};

use crate::{
components::{
timezone::{TimeZone, TimeZoneProvider},
PlainDate, PlainDateTime,
},
builtins::core::{timezone::TimeZone, PlainDate, PlainDateTime},
iso::{IsoDate, IsoDateTime},
options::{
ArithmeticOverflow, Disambiguation, ResolvedRoundingOptions, TemporalRoundingMode,
TemporalUnit,
},
primitive::FiniteF64,
provider::TimeZoneProvider,
rounding::{IncrementRounder, Round},
TemporalError, TemporalResult, TemporalUnwrap, NS_PER_DAY,
};
Expand Down
Loading

0 comments on commit f1154b0

Please sign in to comment.