Skip to content

Commit f1154b0

Browse files
authored
Restructure project to separate core provider APIs from non-provider 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`.
1 parent 59bd045 commit f1154b0

39 files changed

+2510
-935
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ combine = { workspace = true, optional = true }
6767
web-time = { workspace = true, optional = true }
6868

6969
[features]
70-
default = ["now"]
70+
default = ["full"]
7171
log = ["dep:log"]
72-
experimental = ["tzdb"]
72+
full = ["tzdb", "now"]
7373
now = ["std", "dep:web-time"]
7474
tzdb = ["dep:tzif", "std", "dep:jiff-tzdb", "dep:combine"]
7575
std = []

docs/architecture.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Library Architecture
2+
3+
TODO: FFI docs
4+
5+
This doc provides an overview of the layout of `temporal_rs`.
6+
7+
We will go over the Temporal Date/Time builtins, general primitives, and
8+
utiltity crates.
9+
10+
## `temporal_rs` design considerations
11+
12+
`temporal_rs` is first and foremost designed to be a fully spec
13+
compliant implementation of ECMAScript's Temporal date/time builtins.
14+
15+
As such, the main design consideration of `temporal_rs` is that it needs
16+
to be able to service language interpreters / engines.
17+
18+
Thus, `temporal_rs` aims to provide an API along with tools to implement
19+
Temporal while minimizing issue with integrating Temporal into engines.
20+
21+
## Date/Time builtins
22+
23+
The primary date & time builtins/components are located in the
24+
`builtins` directory.
25+
26+
These builtins are then reexported from `lib.rs` to be available from
27+
`temporal_rs`'s root module.
28+
29+
### Core vs. Native
30+
31+
`temporal_rs`'s builtins are split in two distinct directories `core`
32+
and `native`. The core implementation contains the core implementation
33+
of the Temporal builtins; meanwhile, the `native` implementation is a
34+
Rust wrapper around the `core` implementation that simplifies some
35+
"lower" level date/time API that may not be necessary for a general use
36+
case.
37+
38+
### Core implementation
39+
40+
The core implementation is always publicly available, but may not be
41+
available to import from the `temporal_rs`'s root.
42+
43+
The core implementation can be made available from the root by providing
44+
the `--no-default-features` flag.
45+
46+
The core implementation exposes the Provider API that allows the user to
47+
supply a "provider", or any type that implements the `TimeZoneProvider`
48+
trait, for time zone data that the library can use to complete it's
49+
calculations. This is useful from an engine / implementor perspective
50+
because it allows the engine to source time zone data in their preferred
51+
manner without locking them into a library specific implementation that
52+
may or may not have side effects.
53+
54+
A `TimeZoneProvider` API on a core builtin will look like the below.
55+
56+
```rust
57+
impl ZonedDateTime {
58+
pub fn day_with_provider(&self, provider: &impl TimeZoneProvider) -> TemporalResult<u8> {
59+
// Code goes here.
60+
}
61+
}
62+
```
63+
64+
### Native implementation
65+
66+
The native implementation is only available via the "full" default
67+
feature flag.
68+
69+
For the same reason that the Provider API is useful for language
70+
implementors, it is a deterent from a general use case perspective. Most
71+
people using a datetime library, outside of the self-proclaimed time
72+
zone nerds, probably won't care from where their time zone data is being
73+
sourced.
74+
75+
The native Rust wrapper of the core implementation provides a default
76+
provider implementation to remove the need of the user to think or deal
77+
with `TimeZoneProvider`.
78+
79+
```rust
80+
impl ZonedDateTime {
81+
pub fn day(&self) -> TemporalResult<u8> {
82+
// Code goes here.
83+
}
84+
}
85+
```
86+
87+
This greatly simplifies the API for general use cases.
88+
89+
## Primitives
90+
91+
<!-- Should IsoDate, IsoTime, and IsoDateTime be considered primitives? -->
92+
93+
`temporal_rs` has a primitive number implementation `FiniteF64` along
94+
with a few date and time primitives: `IsoDate`, `IsoTime`,
95+
`IsoDateTime`, and `EpochNanoseconds`.
96+
97+
`FiniteF64` allows an interface to translate between ECMAScript's number
98+
type vs. `temporal_rs`'s strictly typed API.
99+
100+
Meanwhile the Date and Time primitives allow certain invariants to be
101+
enforced on their records.
102+
103+
## Utiltiies
104+
105+
`temporal_rs` provides one implementation of the `TimeZoneProvider`
106+
trait: `FsTzdbProvider`.
107+
108+
`FsTzdbProvider` reads from the file systems' tzdb and when not
109+
available on the system, `FsTzdbProvider` relies on a prepackaged
110+
`tzdb`.
111+
112+
<!-- TODO: add some more about parsers -->
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use core::str::FromStr;
1111
use icu_calendar::types::{Era as IcuEra, MonthCode as IcuMonthCode, MonthInfo, YearInfo};
1212

1313
use crate::{
14-
components::{
14+
builtins::core::{
1515
duration::{DateDuration, TimeDuration},
1616
Duration, PlainDate, PlainDateTime, PlainMonthDay, PlainYearMonth,
1717
},
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::iso::{constrain_iso_day, is_valid_iso_day};
99
use crate::options::ArithmeticOverflow;
1010
use crate::{TemporalError, TemporalResult};
1111

12-
use crate::components::{calendar::Calendar, PartialDate};
12+
use crate::builtins::core::{calendar::Calendar, PartialDate};
1313

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

324324
use crate::{
325-
components::{calendar::Calendar, PartialDate},
325+
builtins::core::{calendar::Calendar, PartialDate},
326326
options::ArithmeticOverflow,
327327
};
328328

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
//! This module implements `Date` and any directly related algorithms.
22

33
use crate::{
4-
components::{calendar::Calendar, duration::DateDuration, Duration, PlainDateTime},
4+
builtins::core::{
5+
calendar::Calendar, duration::DateDuration, Duration, PlainDateTime, PlainTime,
6+
},
57
iso::{IsoDate, IsoDateTime, IsoTime},
68
options::{
79
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, DisplayCalendar,
810
ResolvedRoundingOptions, TemporalUnit,
911
},
1012
parsers::{parse_date_time, IxdtfStringBuilder},
1113
primitive::FiniteF64,
14+
provider::NeverProvider,
1215
TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
1316
};
1417
use alloc::{format, string::String};
@@ -17,8 +20,7 @@ use core::str::FromStr;
1720
use super::{
1821
calendar::{ascii_four_to_integer, month_to_month_code},
1922
duration::{normalized::NormalizedDurationRecord, TimeDuration},
20-
timezone::NeverProvider,
21-
PlainMonthDay, PlainTime, PlainYearMonth,
23+
PlainMonthDay, PlainYearMonth,
2224
};
2325
use tinystr::TinyAsciiStr;
2426

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
//! This module implements `DateTime` any directly related algorithms.
22

3+
use super::{
4+
duration::normalized::{NormalizedDurationRecord, NormalizedTimeDuration},
5+
Duration, PartialDate, PartialTime, PlainDate, PlainTime,
6+
};
37
use crate::{
4-
components::{calendar::Calendar, Instant},
8+
builtins::core::{calendar::Calendar, Instant},
59
iso::{IsoDate, IsoDateTime, IsoTime},
610
options::{
711
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, DisplayCalendar,
812
ResolvedRoundingOptions, RoundingOptions, TemporalUnit, ToStringRoundingOptions,
913
},
1014
parsers::{parse_date_time, IxdtfStringBuilder},
15+
provider::NeverProvider,
1116
temporal_assert, TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
1217
};
1318
use alloc::string::String;
1419
use core::{cmp::Ordering, str::FromStr};
15-
16-
use super::{
17-
duration::normalized::{NormalizedDurationRecord, NormalizedTimeDuration},
18-
timezone::NeverProvider,
19-
Duration, PartialDate, PartialTime, PlainDate, PlainTime,
20-
};
2120
use tinystr::TinyAsciiStr;
2221

2322
/// A partial PlainDateTime record
@@ -693,7 +692,7 @@ mod tests {
693692
use tinystr::{tinystr, TinyAsciiStr};
694693

695694
use crate::{
696-
components::{
695+
builtins::core::{
697696
calendar::Calendar, duration::DateDuration, Duration, PartialDate, PartialDateTime,
698697
PartialTime, PlainDateTime,
699698
},
Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
//! This module implements `Duration` along with it's methods and components.
22

33
use crate::{
4-
components::{timezone::TimeZoneProvider, PlainDateTime, PlainTime},
4+
builtins::core::{options::RelativeTo, PlainDateTime, PlainTime, ZonedDateTime},
55
iso::{IsoDateTime, IsoTime},
66
options::{
7-
ArithmeticOverflow, RelativeTo, ResolvedRoundingOptions, RoundingIncrement,
8-
RoundingOptions, TemporalUnit, ToStringRoundingOptions,
7+
ArithmeticOverflow, ResolvedRoundingOptions, RoundingIncrement, RoundingOptions,
8+
TemporalUnit, ToStringRoundingOptions,
99
},
1010
parsers::{FormattableDuration, Precision},
1111
primitive::FiniteF64,
12+
provider::TimeZoneProvider,
1213
temporal_assert, Sign, TemporalError, TemporalResult,
1314
};
1415
use alloc::format;
@@ -25,9 +26,6 @@ use num_traits::AsPrimitive;
2526

2627
use self::normalized::NormalizedTimeDuration;
2728

28-
#[cfg(feature = "experimental")]
29-
use crate::components::timezone::TZ_PROVIDER;
30-
3129
mod date;
3230
pub(crate) mod normalized;
3331
mod time;
@@ -40,8 +38,6 @@ pub use date::DateDuration;
4038
#[doc(inline)]
4139
pub use time::TimeDuration;
4240

43-
use super::ZonedDateTime;
44-
4541
/// A `PartialDuration` is a Duration that may have fields not set.
4642
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
4743
pub struct PartialDuration {
@@ -91,7 +87,7 @@ impl core::fmt::Display for Duration {
9187
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
9288
f.write_str(
9389
&self
94-
.to_temporal_string(ToStringRoundingOptions::default())
90+
.as_temporal_string(ToStringRoundingOptions::default())
9591
.expect("Duration must return a valid string with default options."),
9692
)
9793
}
@@ -639,7 +635,7 @@ impl Duration {
639635
}
640636
}
641637

642-
pub fn to_temporal_string(&self, options: ToStringRoundingOptions) -> TemporalResult<String> {
638+
pub fn as_temporal_string(&self, options: ToStringRoundingOptions) -> TemporalResult<String> {
643639
if options.smallest_unit == Some(TemporalUnit::Hour)
644640
|| options.smallest_unit == Some(TemporalUnit::Minute)
645641
{
@@ -732,20 +728,6 @@ pub fn duration_to_formattable(
732728
})
733729
}
734730

735-
#[cfg(feature = "experimental")]
736-
impl Duration {
737-
pub fn round(
738-
&self,
739-
options: RoundingOptions,
740-
relative_to: Option<RelativeTo>,
741-
) -> TemporalResult<Self> {
742-
let provider = TZ_PROVIDER
743-
.lock()
744-
.map_err(|_| TemporalError::general("Unable to acquire lock"))?;
745-
self.round_with_provider(options, relative_to, &*provider)
746-
}
747-
}
748-
749731
// TODO: Update, optimize, and fix the below. is_valid_duration should probably be generic over a T.
750732

751733
const TWO_POWER_FIFTY_THREE: i128 = 9_007_199_254_740_992;
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,14 @@ use core::{num::NonZeroU128, ops::Add};
55
use num_traits::{AsPrimitive, Euclid, FromPrimitive};
66

77
use crate::{
8-
components::{
9-
timezone::{TimeZone, TimeZoneProvider},
10-
PlainDate, PlainDateTime,
11-
},
8+
builtins::core::{timezone::TimeZone, PlainDate, PlainDateTime},
129
iso::{IsoDate, IsoDateTime},
1310
options::{
1411
ArithmeticOverflow, Disambiguation, ResolvedRoundingOptions, TemporalRoundingMode,
1512
TemporalUnit,
1613
},
1714
primitive::FiniteF64,
15+
provider::TimeZoneProvider,
1816
rounding::{IncrementRounder, Round},
1917
TemporalError, TemporalResult, TemporalUnwrap, NS_PER_DAY,
2018
};

0 commit comments

Comments
 (0)