From d423bba8ef985c9bda6d856326fb696ac6fd7219 Mon Sep 17 00:00:00 2001 From: Kevin <46825870+nekevss@users.noreply.github.com> Date: Sun, 4 Feb 2024 01:21:55 -0500 Subject: [PATCH] Temporal: Refactor Calendar protocol for `JsObject`s (#3651) * Progress on refactor * fix ToTemporalCalendarSlot on to differentiate builtins * Fix rebase regression in custom calendar * Revert to old ToTemporalCalendarSlot --- .../src/builtins/temporal/calendar/mod.rs | 69 ++--- .../src/builtins/temporal/calendar/object.rs | 143 ++++----- .../src/builtins/temporal/plain_date/mod.rs | 228 ++++++++------ .../builtins/temporal/plain_date_time/mod.rs | 229 +++++++++----- .../temporal/plain_date_time/tests.rs | 10 + .../builtins/temporal/plain_month_day/mod.rs | 30 +- .../builtins/temporal/plain_year_month/mod.rs | 32 +- .../builtins/temporal/zoned_date_time/mod.rs | 4 +- core/temporal/src/components/calendar.rs | 99 ++++-- core/temporal/src/components/date.rs | 278 +++++++++-------- core/temporal/src/components/datetime.rs | 286 ++++++++++-------- core/temporal/src/components/month_day.rs | 8 +- core/temporal/src/components/year_month.rs | 11 +- 13 files changed, 848 insertions(+), 579 deletions(-) create mode 100644 core/engine/src/builtins/temporal/plain_date_time/tests.rs diff --git a/core/engine/src/builtins/temporal/calendar/mod.rs b/core/engine/src/builtins/temporal/calendar/mod.rs index 56c6368d1a..5a26709948 100644 --- a/core/engine/src/builtins/temporal/calendar/mod.rs +++ b/core/engine/src/builtins/temporal/calendar/mod.rs @@ -33,15 +33,12 @@ use boa_temporal::{ mod object; -#[doc(inline)] -pub(crate) use object::JsCustomCalendar; - #[cfg(test)] mod tests; /// The `Temporal.Calendar` object. #[derive(Debug, Finalize, JsData)] pub struct Calendar { - slot: CalendarSlot, + slot: CalendarSlot, } unsafe impl Trace for Calendar { @@ -55,7 +52,7 @@ unsafe impl Trace for Calendar { } impl Calendar { - pub(crate) fn new(slot: CalendarSlot) -> Self { + pub(crate) fn new(slot: CalendarSlot) -> Self { Self { slot } } } @@ -155,7 +152,7 @@ impl BuiltInConstructor for Calendar { // 4. Return ? CreateTemporalCalendar(id, NewTarget). create_temporal_calendar( - CalendarSlot::::from_str(&id.to_std_string_escaped())?, + CalendarSlot::::from_str(&id.to_std_string_escaped())?, Some(new_target.clone()), context, ) @@ -956,7 +953,7 @@ impl Calendar { /// 12.2.1 `CreateTemporalCalendar ( identifier [ , newTarget ] )` pub(crate) fn create_temporal_calendar( - identifier: CalendarSlot, + identifier: CalendarSlot, new_target: Option, context: &mut Context, ) -> JsResult { @@ -992,23 +989,21 @@ fn extract_from_temporal_type( zoned_datetime_f: ZDTF, ) -> JsResult> where - DF: FnOnce(&PlainDate) -> JsResult>, - DTF: FnOnce(&PlainDateTime) -> JsResult>, - YMF: FnOnce(&PlainYearMonth) -> JsResult>, - MDF: FnOnce(&PlainMonthDay) -> JsResult>, - ZDTF: FnOnce(&ZonedDateTime) -> JsResult>, + DF: FnOnce(JsObject) -> JsResult>, + DTF: FnOnce(JsObject) -> JsResult>, + YMF: FnOnce(JsObject) -> JsResult>, + MDF: FnOnce(JsObject) -> JsResult>, + ZDTF: FnOnce(JsObject) -> JsResult>, { - let o = object.borrow(); - - if let Some(date) = o.downcast_ref::() { + if let Ok(date) = object.clone().downcast::() { return date_f(date); - } else if let Some(dt) = o.downcast_ref::() { + } else if let Ok(dt) = object.clone().downcast::() { return datetime_f(dt); - } else if let Some(ym) = o.downcast_ref::() { + } else if let Ok(ym) = object.clone().downcast::() { return year_month_f(ym); - } else if let Some(md) = o.downcast_ref::() { + } else if let Ok(md) = object.clone().downcast::() { return month_day_f(md); - } else if let Some(dt) = o.downcast_ref::() { + } else if let Ok(dt) = object.clone().downcast::() { return zoned_datetime_f(dt); } @@ -1020,15 +1015,15 @@ where pub(crate) fn get_temporal_calendar_slot_value_with_default( item: &JsObject, context: &mut Context, -) -> JsResult> { +) -> JsResult> { // 1. If item has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then // a. Return item.[[Calendar]]. if let Some(calendar) = extract_from_temporal_type( item, - |d| Ok(Some(d.inner.calendar().clone())), - |dt| Ok(Some(dt.inner.calendar().clone())), - |ym| Ok(Some(ym.inner.calendar().clone())), - |md| Ok(Some(md.inner.calendar().clone())), + |d| Ok(Some(d.borrow().data().inner.calendar().clone())), + |dt| Ok(Some(dt.borrow().data().inner.calendar().clone())), + |ym| Ok(Some(ym.borrow().data().inner.calendar().clone())), + |md| Ok(Some(md.borrow().data().inner.calendar().clone())), |zdt| { Err(JsNativeError::range() .with_message("Not yet implemented.") @@ -1049,7 +1044,7 @@ pub(crate) fn get_temporal_calendar_slot_value_with_default( pub(crate) fn to_temporal_calendar_slot_value( calendar_like: &JsValue, context: &mut Context, -) -> JsResult> { +) -> JsResult> { // 1. If temporalCalendarLike is undefined and default is present, then // a. Assert: IsBuiltinCalendar(default) is true. // b. Return default. @@ -1061,11 +1056,11 @@ pub(crate) fn to_temporal_calendar_slot_value( // i. Return temporalCalendarLike.[[Calendar]]. if let Some(calendar) = extract_from_temporal_type( calendar_like, - |d| Ok(Some(d.inner.calendar().clone())), - |dt| Ok(Some(dt.inner.calendar().clone())), - |ym| Ok(Some(ym.inner.calendar().clone())), - |md| Ok(Some(md.inner.calendar().clone())), - |zdt| Ok(Some(zdt.inner.calendar().clone())), + |d| Ok(Some(d.borrow().data().inner.calendar().clone())), + |dt| Ok(Some(dt.borrow().data().inner.calendar().clone())), + |ym| Ok(Some(ym.borrow().data().inner.calendar().clone())), + |md| Ok(Some(md.borrow().data().inner.calendar().clone())), + |zdt| Ok(Some(zdt.borrow().data().inner.calendar().clone())), )? { return Ok(calendar); } @@ -1078,10 +1073,8 @@ pub(crate) fn to_temporal_calendar_slot_value( .into()); } - // Types: Box <- UserCalendar - let custom = JsCustomCalendar::new(calendar_like); // c. Return temporalCalendarLike. - return Ok(CalendarSlot::Protocol(custom)); + return Ok(CalendarSlot::Protocol(calendar_like.clone())); } // 3. If temporalCalendarLike is not a String, throw a TypeError exception. @@ -1094,7 +1087,7 @@ pub(crate) fn to_temporal_calendar_slot_value( // 4. Let identifier be ? ParseTemporalCalendarString(temporalCalendarLike). // 5. If IsBuiltinCalendar(identifier) is false, throw a RangeError exception. // 6. Return the ASCII-lowercase of identifier. - Ok(CalendarSlot::::from_str( + Ok(CalendarSlot::::from_str( &calendar_id.to_std_string_escaped(), )?) } @@ -1111,7 +1104,7 @@ fn object_implements_calendar_protocol(calendar_like: &JsObject, context: &mut C fn to_calendar_date_like( date_like: &JsValue, context: &mut Context, -) -> JsResult> { +) -> JsResult> { let Some(obj) = date_like.as_object() else { let date = temporal::plain_date::to_temporal_date(date_like, None, context)?; @@ -1120,9 +1113,9 @@ fn to_calendar_date_like( let Some(date_like) = extract_from_temporal_type( obj, - |d| Ok(Some(CalendarDateLike::Date(d.inner.clone()))), - |dt| Ok(Some(CalendarDateLike::DateTime(dt.inner.clone()))), - |ym| Ok(Some(CalendarDateLike::YearMonth(ym.inner.clone()))), + |d| Ok(Some(CalendarDateLike::CustomDate(d))), + |dt| Ok(Some(CalendarDateLike::CustomDateTime(dt))), + |ym| Ok(Some(CalendarDateLike::CustomYearMonth(ym))), |_| Ok(None), |_| Ok(None), )? diff --git a/core/engine/src/builtins/temporal/calendar/object.rs b/core/engine/src/builtins/temporal/calendar/object.rs index bbb93de6d7..64e0ef3d99 100644 --- a/core/engine/src/builtins/temporal/calendar/object.rs +++ b/core/engine/src/builtins/temporal/calendar/object.rs @@ -14,11 +14,10 @@ use crate::{ }; use std::any::Any; -use boa_gc::{Finalize, Trace}; use boa_macros::utf16; use boa_temporal::{ components::{ - calendar::{CalendarDateLike, CalendarProtocol}, + calendar::{CalendarDateLike, CalendarProtocol, DateTypes}, Date, Duration, MonthDay, YearMonth, }, options::ArithmeticOverflow, @@ -26,28 +25,23 @@ use boa_temporal::{ }; use num_traits::ToPrimitive; use plain_date::PlainDate; +use plain_date_time::PlainDateTime; use plain_month_day::PlainMonthDay; use plain_year_month::PlainYearMonth; -/// A user-defined, custom calendar that is only known at runtime -/// and executed at runtime. -/// -/// A user-defined calendar implements all of the `CalendarProtocolMethods` -/// and therefore satisfies the requirements to be used as a calendar. -#[derive(Debug, Clone, Trace, Finalize)] -pub(crate) struct JsCustomCalendar { - calendar: JsObject, -} +/// The custom data types for a Custom `JsObject` Calendar. +#[derive(Debug, Clone, Copy)] +pub struct CustomDateLikes; -impl JsCustomCalendar { - pub(crate) fn new(calendar: &JsObject) -> Self { - Self { - calendar: calendar.clone(), - } - } +impl DateTypes for CustomDateLikes { + type Date = JsObject; + type DateTime = JsObject; + type YearMonth = JsObject; + type MonthDay = JsObject; } -impl CalendarProtocol for JsCustomCalendar { +impl CalendarProtocol for JsObject { + type DateLikes = CustomDateLikes; fn date_from_fields( &self, fields: &mut TemporalFields, @@ -59,7 +53,6 @@ impl CalendarProtocol for JsCustomCalendar { .expect("Context was not provided for a CustomCalendar."); let method = self - .calendar .get(utf16!("dateFromFields"), context) .expect("method must exist on a object that implements the CalendarProtocol."); @@ -82,7 +75,7 @@ impl CalendarProtocol for JsCustomCalendar { TemporalError::general("dateFromFields must be implemented as a callable method.") })? .call( - &self.calendar.clone().into(), + &self.clone().into(), &[fields.into(), overflow_obj.into()], context, ) @@ -105,13 +98,12 @@ impl CalendarProtocol for JsCustomCalendar { fields: &mut TemporalFields, overflow: ArithmeticOverflow, context: &mut dyn Any, - ) -> TemporalResult> { + ) -> TemporalResult> { let context = context .downcast_mut::() .expect("Context was not provided for a CustomCalendar."); let method = self - .calendar .get(utf16!("yearMonthFromFields"), context) .expect("method must exist on a object that implements the CalendarProtocol."); @@ -136,7 +128,7 @@ impl CalendarProtocol for JsCustomCalendar { ) })? .call( - &self.calendar.clone().into(), + &self.clone().into(), &[fields.into(), overflow_obj.into()], context, ) @@ -159,13 +151,12 @@ impl CalendarProtocol for JsCustomCalendar { fields: &mut TemporalFields, overflow: ArithmeticOverflow, context: &mut dyn Any, - ) -> TemporalResult> { + ) -> TemporalResult> { let context = context .downcast_mut::() .expect("Context was not provided for a CustomCalendar."); let method = self - .calendar .get(utf16!("yearMonthFromFields"), context) .expect("method must exist on a object that implements the CalendarProtocol."); @@ -190,7 +181,7 @@ impl CalendarProtocol for JsCustomCalendar { ) })? .call( - &self.calendar.clone().into(), + &self.clone().into(), &[fields.into(), overflow_obj.into()], context, ) @@ -210,19 +201,19 @@ impl CalendarProtocol for JsCustomCalendar { fn date_add( &self, - _date: &Date, + _date: &Date, _duration: &Duration, _overflow: ArithmeticOverflow, _context: &mut dyn Any, - ) -> TemporalResult> { + ) -> TemporalResult> { // TODO Err(TemporalError::general("Not yet implemented.")) } fn date_until( &self, - _one: &Date, - _two: &Date, + _one: &Date, + _two: &Date, _largest_unit: boa_temporal::options::TemporalUnit, _context: &mut dyn Any, ) -> TemporalResult { @@ -232,7 +223,7 @@ impl CalendarProtocol for JsCustomCalendar { fn era( &self, - _: &CalendarDateLike, + _: &CalendarDateLike, _: &mut dyn Any, ) -> TemporalResult>> { // Return undefined as custom calendars do not implement -> Currently. @@ -241,7 +232,7 @@ impl CalendarProtocol for JsCustomCalendar { fn era_year( &self, - _: &CalendarDateLike, + _: &CalendarDateLike, _: &mut dyn Any, ) -> TemporalResult> { // Return undefined as custom calendars do not implement -> Currently. @@ -250,7 +241,7 @@ impl CalendarProtocol for JsCustomCalendar { fn year( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -260,14 +251,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("year")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; // Validate the return value. @@ -297,7 +287,7 @@ impl CalendarProtocol for JsCustomCalendar { fn month( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -307,14 +297,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("month")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; // Validate the return value. @@ -344,7 +333,7 @@ impl CalendarProtocol for JsCustomCalendar { fn month_code( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult> { let context = context @@ -354,14 +343,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("monthCode")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; let JsValue::String(result) = val else { @@ -376,7 +364,7 @@ impl CalendarProtocol for JsCustomCalendar { fn day( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -386,14 +374,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("day")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; // Validate the return value. @@ -423,7 +410,7 @@ impl CalendarProtocol for JsCustomCalendar { fn day_of_week( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -433,14 +420,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("dayOfWeek")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; // Validate the return value. @@ -472,7 +458,7 @@ impl CalendarProtocol for JsCustomCalendar { fn day_of_year( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -482,14 +468,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("dayOfYear")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; // Validate the return value. @@ -521,7 +506,7 @@ impl CalendarProtocol for JsCustomCalendar { fn week_of_year( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -531,14 +516,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("weekOfYear")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; // Validate the return value. @@ -570,7 +554,7 @@ impl CalendarProtocol for JsCustomCalendar { fn year_of_week( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -580,14 +564,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("yearOfWeek")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; // Validate the return value. @@ -612,7 +595,7 @@ impl CalendarProtocol for JsCustomCalendar { fn days_in_week( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -622,14 +605,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("daysInWeek")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; // Validate the return value. @@ -661,7 +643,7 @@ impl CalendarProtocol for JsCustomCalendar { fn days_in_month( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -671,13 +653,12 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("daysInMonth")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; // Validate the return value. @@ -711,7 +692,7 @@ impl CalendarProtocol for JsCustomCalendar { fn days_in_year( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -721,14 +702,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("daysInYear")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; // Validate the return value. @@ -760,7 +740,7 @@ impl CalendarProtocol for JsCustomCalendar { fn months_in_year( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -770,14 +750,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("monthsInYear")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; // Validate the return value. @@ -811,7 +790,7 @@ impl CalendarProtocol for JsCustomCalendar { fn in_leap_year( &self, - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut dyn Any, ) -> TemporalResult { let context = context @@ -821,14 +800,13 @@ impl CalendarProtocol for JsCustomCalendar { let date_like = date_like_to_object(date_like, context)?; let method = self - .calendar .get(PropertyKey::from(utf16!("inLeapYear")), context) .expect("method must exist on a object that implements the CalendarProtocol."); let val = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[date_like], context) + .call(&self.clone().into(), &[date_like], context) .map_err(|err| TemporalError::general(err.to_string()))?; let JsValue::Boolean(result) = val else { @@ -851,14 +829,13 @@ impl CalendarProtocol for JsCustomCalendar { ); let method = self - .calendar .get(PropertyKey::from(utf16!("fields")), context) .expect("method must exist on an object that implements the CalendarProtocol."); let result = method .as_callable() .expect("is method") - .call(&self.calendar.clone().into(), &[fields_js.into()], context) + .call(&self.clone().into(), &[fields_js.into()], context) .map_err(|e| TemporalError::general(e.to_string()))?; // validate result and map to a `Vec` @@ -902,7 +879,6 @@ impl CalendarProtocol for JsCustomCalendar { .map_err(|e| TemporalError::general(e.to_string()))?; let method = self - .calendar .get(PropertyKey::from(utf16!("mergeFields")), context) .expect("method must exist on an object that implements the CalendarProtocol."); @@ -910,7 +886,7 @@ impl CalendarProtocol for JsCustomCalendar { .as_callable() .expect("is method") .call( - &self.calendar.clone().into(), + &self.clone().into(), &[fields.into(), add_fields.into()], context, ) @@ -931,10 +907,9 @@ impl CalendarProtocol for JsCustomCalendar { .expect("Context was not provided for a CustomCalendar."); let identifier = self - .calendar .__get__( &PropertyKey::from(utf16!("id")), - self.calendar.clone().into(), + self.clone().into(), &mut context.into(), ) .expect("method must exist on a object that implements the CalendarProtocol."); @@ -949,7 +924,7 @@ impl CalendarProtocol for JsCustomCalendar { /// Utility function for converting `Temporal`'s `CalendarDateLike` to it's `Boa` specific `JsObject`. pub(crate) fn date_like_to_object( - date_like: &CalendarDateLike, + date_like: &CalendarDateLike, context: &mut Context, ) -> TemporalResult { match date_like { @@ -961,13 +936,9 @@ pub(crate) fn date_like_to_object( .map_err(|e| TemporalError::general(e.to_string())) .map(Into::into) } - CalendarDateLike::MonthDay(md) => { - plain_month_day::create_temporal_month_day(md.clone(), None, context) - .map_err(|e| TemporalError::general(e.to_string())) - } - CalendarDateLike::YearMonth(ym) => { - plain_year_month::create_temporal_year_month(ym.clone(), None, context) - .map_err(|e| TemporalError::general(e.to_string())) - } + CalendarDateLike::CustomMonthDay(md) => Ok(md.clone().upcast().into()), + CalendarDateLike::CustomYearMonth(ym) => Ok(ym.clone().upcast().into()), + CalendarDateLike::CustomDate(pd) => Ok(pd.clone().upcast().into()), + CalendarDateLike::CustomDateTime(pdt) => Ok(pdt.clone().upcast().into()), } } diff --git a/core/engine/src/builtins/temporal/plain_date/mod.rs b/core/engine/src/builtins/temporal/plain_date/mod.rs index beeba836f6..596e1bf400 100644 --- a/core/engine/src/builtins/temporal/plain_date/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date/mod.rs @@ -19,25 +19,41 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; use boa_temporal::{ - components::{Date as InnerDate, DateTime}, + components::{ + calendar::{CalendarSlot, GetCalendarSlot}, + Date as InnerDate, DateTime, + }, + iso::IsoDateSlots, options::ArithmeticOverflow, }; -use super::{calendar, create_temporal_calendar, JsCustomCalendar, PlainDateTime, ZonedDateTime}; +use super::{calendar, create_temporal_calendar, PlainDateTime, ZonedDateTime}; /// The `Temporal.PlainDate` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] #[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerDate` could contain `Trace` types. pub struct PlainDate { - pub(crate) inner: InnerDate, + pub(crate) inner: InnerDate, } impl PlainDate { - pub(crate) fn new(inner: InnerDate) -> Self { + pub(crate) fn new(inner: InnerDate) -> Self { Self { inner } } } +impl IsoDateSlots for JsObject { + fn iso_date(&self) -> boa_temporal::iso::IsoDate { + self.borrow().data().inner.iso() + } +} + +impl GetCalendarSlot for JsObject { + fn get_calendar(&self) -> CalendarSlot { + self.borrow().data().inner.get_calendar() + } +} + impl BuiltInObject for PlainDate { const NAME: JsString = StaticJsStrings::PLAIN_DATE; } @@ -258,110 +274,140 @@ impl PlainDate { /// 3.3.4 get `Temporal.PlainDate.prototype.year` fn get_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; - Ok(date.inner.contextual_year(context)?.into()) + Ok(InnerDate::::contextualized_year(&date, context)?.into()) } /// 3.3.5 get `Temporal.PlainDate.prototype.month` fn get_month(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(date.inner.contextual_month(context)?.into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; + + Ok(InnerDate::::contextualized_month(&date, context)?.into()) } /// 3.3.6 get Temporal.PlainDate.prototype.monthCode fn get_month_code(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; - Ok(JsString::from(date.inner.contextual_month_code(context)?.as_str()).into()) + Ok(JsString::from( + InnerDate::::contextualized_month_code(&date, context)?.as_str(), + ) + .into()) } /// 3.3.7 get `Temporal.PlainDate.prototype.day` fn get_day(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(date.inner.contextual_day(context)?.into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; + + Ok(InnerDate::::contextualized_day(&date, context)?.into()) } /// 3.3.8 get `Temporal.PlainDate.prototype.dayOfWeek` fn get_day_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; - Ok(date.inner.contextual_day_of_week(context)?.into()) + Ok(InnerDate::::contextualized_day_of_week(&date, context)?.into()) } /// 3.3.9 get `Temporal.PlainDate.prototype.dayOfYear` fn get_day_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(date.inner.contextual_day_of_year(context)?.into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; + + Ok(InnerDate::::contextualized_day_of_year(&date, context)?.into()) } /// 3.3.10 get `Temporal.PlainDate.prototype.weekOfYear` fn get_week_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; - Ok(date.inner.contextual_week_of_year(context)?.into()) + Ok(InnerDate::::contextualized_week_of_year(&date, context)?.into()) } /// 3.3.11 get `Temporal.PlainDate.prototype.yearOfWeek` fn get_year_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(date.inner.contextual_year_of_week(context)?.into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; + + Ok(InnerDate::::contextualized_year_of_week(&date, context)?.into()) } /// 3.3.12 get `Temporal.PlainDate.prototype.daysInWeek` fn get_days_in_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(date.inner.contextual_days_in_week(context)?.into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; + + Ok(InnerDate::::contextualized_days_in_week(&date, context)?.into()) } /// 3.3.13 get `Temporal.PlainDate.prototype.daysInMonth` @@ -370,26 +416,32 @@ impl PlainDate { _: &[JsValue], context: &mut Context, ) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(date.inner.contextual_days_in_month(context)?.into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; + + Ok(InnerDate::::contextualized_days_in_month(&date, context)?.into()) } /// 3.3.14 get `Temporal.PlainDate.prototype.daysInYear` fn get_days_in_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; - Ok(date.inner.contextual_days_in_year(context)?.into()) + Ok(InnerDate::::contextualized_days_in_year(&date, context)?.into()) } /// 3.3.15 get `Temporal.PlainDate.prototype.monthsInYear` @@ -398,26 +450,32 @@ impl PlainDate { _: &[JsValue], context: &mut Context, ) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; - Ok(date.inner.contextual_months_in_year(context)?.into()) + Ok(InnerDate::::contextualized_months_in_year(&date, context)?.into()) } /// 3.3.16 get `Temporal.PlainDate.prototype.inLeapYear` fn get_in_leap_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDate object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDate object.") + .into()); + }; - Ok(date.inner.contextual_in_leap_year(context)?.into()) + Ok(InnerDate::::contextualized_in_leap_year(&date, context)?.into()) } } @@ -511,7 +569,7 @@ impl PlainDate { /// 3.5.3 `CreateTemporalDate ( isoYear, isoMonth, isoDay, calendar [ , newTarget ] )` pub(crate) fn create_temporal_date( - inner: InnerDate, + inner: InnerDate, new_target: Option<&JsValue>, context: &mut Context, ) -> JsResult { @@ -519,7 +577,7 @@ pub(crate) fn create_temporal_date( // 1. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception. // 2. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception. - if !DateTime::::validate(&inner) { + if !DateTime::::validate(&inner) { return Err(JsNativeError::range() .with_message("Date is not within ISO date time limits.") .into()); @@ -620,7 +678,7 @@ pub(crate) fn to_temporal_date( // 13. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar). let result = date_like_string .to_std_string_escaped() - .parse::>() + .parse::>() .map_err(|err| JsNativeError::range().with_message(err.to_string()))?; Ok(PlainDate::new(result)) diff --git a/core/engine/src/builtins/temporal/plain_date_time/mod.rs b/core/engine/src/builtins/temporal/plain_date_time/mod.rs index 31e994795c..143553edf2 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/mod.rs @@ -17,27 +17,46 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; -use boa_temporal::components::DateTime as InnerDateTime; +#[cfg(test)] +mod tests; -use super::JsCustomCalendar; +use boa_temporal::{ + components::{ + calendar::{CalendarSlot, GetCalendarSlot}, + DateTime as InnerDateTime, + }, + iso::{IsoDate, IsoDateSlots}, +}; /// The `Temporal.PlainDateTime` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] #[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerDateTime` could contain `Trace` types. pub struct PlainDateTime { - pub(crate) inner: InnerDateTime, + pub(crate) inner: InnerDateTime, } impl PlainDateTime { - fn new(inner: InnerDateTime) -> Self { + fn new(inner: InnerDateTime) -> Self { Self { inner } } - pub(crate) fn inner(&self) -> &InnerDateTime { + pub(crate) fn inner(&self) -> &InnerDateTime { &self.inner } } +impl IsoDateSlots for JsObject { + fn iso_date(&self) -> IsoDate { + self.borrow().data().inner.iso_date() + } +} + +impl GetCalendarSlot for JsObject { + fn get_calendar(&self) -> CalendarSlot { + self.borrow().data().inner.get_calendar() + } +} + impl BuiltInObject for PlainDateTime { const NAME: JsString = StaticJsStrings::PLAIN_DATETIME; } @@ -343,50 +362,65 @@ impl PlainDateTime { /// 5.3.4 get `Temporal.PlainDateTime.prototype.year` fn get_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; - Ok(date.inner.contextual_year(context)?.into()) + Ok(InnerDateTime::::contextualized_year(&date, context)?.into()) } /// 5.3.5 get `Temporal.PlainDateTime.prototype.month` fn get_month(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; - Ok(date.inner.contextual_month(context)?.into()) + Ok(InnerDateTime::::contextualized_month(&date, context)?.into()) } /// 5.3.6 get Temporal.PlainDateTime.prototype.monthCode fn get_month_code(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(JsString::from(date.inner.contextual_month_code(context)?.as_str()).into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; + + Ok(JsString::from( + InnerDateTime::::contextualized_month_code(&date, context)?.as_str(), + ) + .into()) } /// 5.3.7 get `Temporal.PlainDateTime.prototype.day` fn get_day(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(date.inner.contextual_day(context)?.into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; + + Ok(InnerDateTime::::contextualized_day(&date, context)?.into()) } /// 5.3.8 get `Temporal.PlainDateTime.prototype.hour` @@ -481,62 +515,77 @@ impl PlainDateTime { /// 5.3.14 get `Temporal.PlainDateTime.prototype.dayOfWeek` fn get_day_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; - Ok(date.inner.contextual_day_of_week(context)?.into()) + Ok(InnerDateTime::::contextualized_day_of_week(&date, context)?.into()) } /// 5.3.15 get `Temporal.PlainDateTime.prototype.dayOfYear` fn get_day_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(date.inner.contextual_day_of_year(context)?.into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; + + Ok(InnerDateTime::::contextualized_day_of_year(&date, context)?.into()) } /// 5.3.16 get `Temporal.PlainDateTime.prototype.weekOfYear` fn get_week_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; - Ok(date.inner.contextual_week_of_year(context)?.into()) + Ok(InnerDateTime::::contextualized_week_of_year(&date, context)?.into()) } /// 5.3.17 get `Temporal.PlainDateTime.prototype.yearOfWeek` fn get_year_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(date.inner.contextual_year_of_week(context)?.into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; + + Ok(InnerDateTime::::contextualized_year_of_week(&date, context)?.into()) } /// 5.3.18 get `Temporal.PlainDateTime.prototype.daysInWeek` fn get_days_in_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(date.inner.contextual_days_in_week(context)?.into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; + + Ok(InnerDateTime::::contextualized_days_in_week(&date, context)?.into()) } /// 5.3.19 get `Temporal.PlainDateTime.prototype.daysInMonth` @@ -545,26 +594,32 @@ impl PlainDateTime { _: &[JsValue], context: &mut Context, ) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; - Ok(date.inner.contextual_days_in_month(context)?.into()) + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; + + Ok(InnerDateTime::::contextualized_days_in_month(&date, context)?.into()) } /// 5.3.20 get `Temporal.PlainDateTime.prototype.daysInYear` fn get_days_in_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; - Ok(date.inner.contextual_days_in_year(context)?.into()) + Ok(InnerDateTime::::contextualized_days_in_year(&date, context)?.into()) } /// 5.3.21 get `Temporal.PlainDateTime.prototype.monthsInYear` @@ -573,33 +628,39 @@ impl PlainDateTime { _: &[JsValue], context: &mut Context, ) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; - Ok(date.inner.contextual_months_in_year(context)?.into()) + Ok(InnerDateTime::::contextualized_months_in_year(&date, context)?.into()) } /// 5.3.22 get `Temporal.PlainDateTime.prototype.inLeapYear` fn get_in_leap_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { - let date = this + let obj = this .as_object() - .and_then(JsObject::downcast_ref::) - .ok_or_else(|| { - JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") - })?; + .ok_or_else(|| JsNativeError::typ().with_message("this must be an object."))?; + + let Ok(date) = obj.clone().downcast::() else { + return Err(JsNativeError::typ() + .with_message("the this object must be a PlainDateTime object.") + .into()); + }; - Ok(date.inner.contextual_in_leap_year(context)?.into()) + Ok(InnerDateTime::::contextualized_in_leap_year(&date, context)?.into()) } } // ==== `PlainDateTime` Abstract Operations` ==== pub(crate) fn create_temporal_datetime( - inner: InnerDateTime, + inner: InnerDateTime, new_target: Option<&JsValue>, context: &mut Context, ) -> JsResult { diff --git a/core/engine/src/builtins/temporal/plain_date_time/tests.rs b/core/engine/src/builtins/temporal/plain_date_time/tests.rs new file mode 100644 index 0000000000..4cf106bcfc --- /dev/null +++ b/core/engine/src/builtins/temporal/plain_date_time/tests.rs @@ -0,0 +1,10 @@ +use crate::{run_test_actions, TestAction}; + +#[test] +fn pdt_year_of_week_basic() { + run_test_actions([ + TestAction::run("let calendar = Temporal.Calendar.from('iso8601')"), + TestAction::run("let pdt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, calendar)"), + TestAction::assert_eq("pdt.yearOfWeek", 1976), + ]); +} diff --git a/core/engine/src/builtins/temporal/plain_month_day/mod.rs b/core/engine/src/builtins/temporal/plain_month_day/mod.rs index a4a664ccad..51a2b0978f 100644 --- a/core/engine/src/builtins/temporal/plain_month_day/mod.rs +++ b/core/engine/src/builtins/temporal/plain_month_day/mod.rs @@ -12,23 +12,39 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; -use boa_temporal::components::{DateTime, MonthDay as InnerMonthDay}; - -use super::JsCustomCalendar; +use boa_temporal::{ + components::{ + calendar::{CalendarSlot, GetCalendarSlot}, + DateTime, MonthDay as InnerMonthDay, + }, + iso::IsoDateSlots, +}; /// The `Temporal.PlainMonthDay` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] #[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerMonthDay` could contain `Trace` types. pub struct PlainMonthDay { - pub(crate) inner: InnerMonthDay, + pub(crate) inner: InnerMonthDay, } impl PlainMonthDay { - fn new(inner: InnerMonthDay) -> Self { + fn new(inner: InnerMonthDay) -> Self { Self { inner } } } +impl IsoDateSlots for JsObject { + fn iso_date(&self) -> boa_temporal::iso::IsoDate { + self.borrow().data().inner.iso_date() + } +} + +impl GetCalendarSlot for JsObject { + fn get_calendar(&self) -> CalendarSlot { + self.borrow().data().inner.get_calendar() + } +} + impl BuiltInObject for PlainMonthDay { const NAME: JsString = StaticJsStrings::PLAIN_MD; } @@ -71,13 +87,13 @@ impl BuiltInConstructor for PlainMonthDay { // ==== `PlainMonthDay` Abstract Operations ==== pub(crate) fn create_temporal_month_day( - inner: InnerMonthDay, + inner: InnerMonthDay, new_target: Option<&JsValue>, context: &mut Context, ) -> JsResult { // 1. If IsValidISODate(referenceISOYear, isoMonth, isoDay) is false, throw a RangeError exception. // 2. If ISODateTimeWithinLimits(referenceISOYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception. - if DateTime::::validate(&inner) { + if DateTime::::validate(&inner) { return Err(JsNativeError::range() .with_message("PlainMonthDay is not a valid ISO date time.") .into()); diff --git a/core/engine/src/builtins/temporal/plain_year_month/mod.rs b/core/engine/src/builtins/temporal/plain_year_month/mod.rs index 97b580d2f8..1f47fcd05a 100644 --- a/core/engine/src/builtins/temporal/plain_year_month/mod.rs +++ b/core/engine/src/builtins/temporal/plain_year_month/mod.rs @@ -13,22 +13,44 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; -use super::{calendar::to_temporal_calendar_slot_value, JsCustomCalendar}; -use boa_temporal::{components::YearMonth as InnerYearMonth, options::ArithmeticOverflow}; +use super::calendar::to_temporal_calendar_slot_value; + +use boa_temporal::{ + iso::IsoDateSlots, + { + components::{ + calendar::{CalendarSlot, GetCalendarSlot}, + YearMonth as InnerYearMonth, + }, + options::ArithmeticOverflow, + }, +}; /// The `Temporal.PlainYearMonth` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] #[boa_gc(unsafe_empty_trace)] // TODO: Remove this!!! `InnerYearMonth` could contain `Trace` types. pub struct PlainYearMonth { - pub(crate) inner: InnerYearMonth, + pub(crate) inner: InnerYearMonth, } impl PlainYearMonth { - pub(crate) fn new(inner: InnerYearMonth) -> Self { + pub(crate) fn new(inner: InnerYearMonth) -> Self { Self { inner } } } +impl IsoDateSlots for JsObject { + fn iso_date(&self) -> boa_temporal::iso::IsoDate { + self.borrow().data().inner.iso_date() + } +} + +impl GetCalendarSlot for JsObject { + fn get_calendar(&self) -> CalendarSlot { + self.borrow().data().inner.get_calendar() + } +} + impl BuiltInObject for PlainYearMonth { const NAME: JsString = StaticJsStrings::PLAIN_YM; } @@ -279,7 +301,7 @@ impl PlainYearMonth { // 9.5.5 `CreateTemporalYearMonth ( isoYear, isoMonth, calendar, referenceISODay [ , newTarget ] )` pub(crate) fn create_temporal_year_month( - ym: InnerYearMonth, + ym: InnerYearMonth, new_target: Option<&JsValue>, context: &mut Context, ) -> JsResult { diff --git a/core/engine/src/builtins/temporal/zoned_date_time/mod.rs b/core/engine/src/builtins/temporal/zoned_date_time/mod.rs index b1e089c8e2..d023e04c48 100644 --- a/core/engine/src/builtins/temporal/zoned_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/zoned_date_time/mod.rs @@ -14,12 +14,12 @@ use boa_temporal::components::{ ZonedDateTime as InnerZdt, }; -use super::{JsCustomCalendar, JsCustomTimeZone}; +use super::JsCustomTimeZone; /// The `Temporal.ZonedDateTime` object. #[derive(Debug, Clone, Finalize, JsData)] pub struct ZonedDateTime { - pub(crate) inner: InnerZdt, + pub(crate) inner: InnerZdt, } unsafe impl Trace for ZonedDateTime { diff --git a/core/temporal/src/components/calendar.rs b/core/temporal/src/components/calendar.rs index 03cc57341f..22b9f3e6cd 100644 --- a/core/temporal/src/components/calendar.rs +++ b/core/temporal/src/components/calendar.rs @@ -76,17 +76,29 @@ impl From<&[String]> for CalendarFieldsType { } } +// NOTE (nekevss): May be worth switching the below to "Custom" `DateLikes`, and +// allow the non-custom to be engine specific types. +// +// enum CalendarDateLike> { +// Date(Date), +// CustomDate(D::Date), +// ... +// } /// The `DateLike` objects that can be provided to the `CalendarProtocol`. #[derive(Debug)] pub enum CalendarDateLike { - /// Represents a `Date` datelike - Date(Date), - /// Represents a `DateTime` datelike + /// Represents a user-defined `Date` datelike + CustomDate(<::DateLikes as DateTypes>::Date), + /// Represents a user-defined `DateTime` datelike + CustomDateTime(<::DateLikes as DateTypes>::DateTime), + /// Represents a user-defined `YearMonth` datelike + CustomYearMonth(<::DateLikes as DateTypes>::YearMonth), + /// Represents a user-defined `MonthDay` datelike + CustomMonthDay(<::DateLikes as DateTypes>::MonthDay), + /// Represents a `DateTime`. DateTime(DateTime), - /// Represents a `YearMonth` datelike - YearMonth(YearMonth), - /// Represents a `MonthDay` datelike - MonthDay(MonthDay), + /// Represents a `Date`. + Date(Date), } impl CalendarDateLike { @@ -95,18 +107,35 @@ impl CalendarDateLike { #[must_use] pub fn as_iso_date(&self) -> IsoDate { match self { + CalendarDateLike::CustomDate(d) => d.iso_date(), + CalendarDateLike::CustomMonthDay(md) => md.iso_date(), + CalendarDateLike::CustomYearMonth(ym) => ym.iso_date(), + CalendarDateLike::CustomDateTime(dt) => dt.iso_date(), + CalendarDateLike::DateTime(dt) => dt.iso_date(), CalendarDateLike::Date(d) => d.iso_date(), - CalendarDateLike::DateTime(dt) => *dt.iso_date(), - CalendarDateLike::MonthDay(md) => md.iso_date(), - CalendarDateLike::YearMonth(ym) => ym.iso_date(), } } } +// TODO: DateTypes should implement a trait -> `ToTemporalDate`: `GetCalendarSlot` +/// A trait for implementing `DateLike` types +pub trait DateTypes { + /// A Custom `Date` Type for an associated `CalendarProtocol`. Default `Date` + type Date: IsoDateSlots + GetCalendarSlot + Clone + core::fmt::Debug; + /// A Custom `DateTime` Type for an associated `CalendarProtocol`. Default `DateTime` + type DateTime: IsoDateSlots + GetCalendarSlot + Clone + core::fmt::Debug; + /// A Custom `YearMonth` Type for an associated `CalendarProtocol`. Default `YearMonth` + type YearMonth: IsoDateSlots + GetCalendarSlot + Clone + core::fmt::Debug; + /// A Custom `MonthDay` Type for an associated `CalendarProtocol`. Default `MonthDay` + type MonthDay: IsoDateSlots + GetCalendarSlot + Clone + core::fmt::Debug; +} + // ==== CalendarProtocol trait ==== /// A trait for implementing a Builtin Calendar's Calendar Protocol in Rust. pub trait CalendarProtocol: Clone { + /// Registers a valid set of custom `CalendarDateLike` values. + type DateLikes: DateTypes; /// Creates a `Temporal.PlainDate` object from provided fields. fn date_from_fields( &self, @@ -244,6 +273,12 @@ pub trait CalendarProtocol: Clone { fn identifier(&self, context: &mut dyn Any) -> TemporalResult; } +/// A trait for retrieving an internal calendar slice. +pub trait GetCalendarSlot { + /// Returns the `CalendarSlot` value of the implementor. + fn get_calendar(&self) -> CalendarSlot; +} + // NOTE(nekevss): Builtin could be `Rc`, but doing so may // have an effect on the pattern matching for `CalendarSlot`'s methods. /// The `[[Calendar]]` field slot of a Temporal Object. @@ -816,12 +851,26 @@ impl CalendarSlot { } } +impl IsoDateSlots for () { + fn iso_date(&self) -> IsoDate { + unreachable!() + } +} + +impl DateTypes<()> for () { + type Date = Date<()>; + type DateTime = DateTime<()>; + type YearMonth = YearMonth<()>; + type MonthDay = MonthDay<()>; +} + /// An empty `CalendarProtocol` implementation on `()`. /// /// # Panics /// /// Attempting to use this empty calendar implementation as a valid calendar is an error and will cause a panic. impl CalendarProtocol for () { + type DateLikes = (); fn date_from_fields( &self, _: &mut TemporalFields, @@ -871,69 +920,69 @@ impl CalendarProtocol for () { fn era( &self, - _: &CalendarDateLike<()>, + _: &CalendarDateLike, _: &mut dyn Any, ) -> TemporalResult>> { unreachable!(); } - fn era_year(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult> { + fn era_year(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult> { unreachable!(); } - fn year(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn year(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } - fn month(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn month(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } fn month_code( &self, - _: &CalendarDateLike<()>, + _: &CalendarDateLike, _: &mut dyn Any, ) -> TemporalResult> { unreachable!(); } - fn day(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn day(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } - fn day_of_week(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn day_of_week(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } - fn day_of_year(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn day_of_year(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } - fn week_of_year(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn week_of_year(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } - fn year_of_week(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn year_of_week(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } - fn days_in_week(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn days_in_week(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } - fn days_in_month(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn days_in_month(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } - fn days_in_year(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn days_in_year(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } - fn months_in_year(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn months_in_year(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } - fn in_leap_year(&self, _: &CalendarDateLike<()>, _: &mut dyn Any) -> TemporalResult { + fn in_leap_year(&self, _: &CalendarDateLike, _: &mut dyn Any) -> TemporalResult { unreachable!(); } diff --git a/core/temporal/src/components/date.rs b/core/temporal/src/components/date.rs index 6f8d5f647a..0b768035f2 100644 --- a/core/temporal/src/components/date.rs +++ b/core/temporal/src/components/date.rs @@ -15,7 +15,10 @@ use crate::{ }; use std::{any::Any, str::FromStr}; -use super::duration::TimeDuration; +use super::{ + calendar::{CalendarDateLike, DateTypes, GetCalendarSlot}, + duration::TimeDuration, +}; /// The native Rust implementation of `Temporal.PlainDate`. #[derive(Debug, Default, Clone)] @@ -67,7 +70,7 @@ impl Date { /// Creates a `Date` from a `DateTime`. pub fn from_datetime(dt: &DateTime) -> Self { Self { - iso: *dt.iso_date(), + iso: dt.iso_date(), calendar: dt.calendar().clone(), } } @@ -127,174 +130,211 @@ impl Date { // ==== Calendar-derived Public API ==== -impl Date { - /// Returns the calendar year value with provided context. - pub fn contextual_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.year( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) - } - +impl Date<()> { /// Returns the calendar year value. pub fn year(&self) -> TemporalResult { - self.contextual_year(&mut ()) - } - - /// Returns the calendar month value with provided context. - pub fn contextual_month(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.month( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + self.calendar + .year(&CalendarDateLike::Date(self.clone()), &mut ()) } /// Returns the calendar month value. pub fn month(&self) -> TemporalResult { - self.contextual_month(&mut ()) - } - - /// Returns the calendar month code value with provided context. - pub fn contextual_month_code(&self, context: &mut dyn Any) -> TemporalResult> { - self.calendar.month_code( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + self.calendar + .month(&CalendarDateLike::Date(self.clone()), &mut ()) } /// Returns the calendar month code value. pub fn month_code(&self) -> TemporalResult> { - self.contextual_month_code(&mut ()) - } - - /// Returns the calendar day value with provided context. - pub fn contextual_day(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.day( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + self.calendar + .month_code(&CalendarDateLike::Date(self.clone()), &mut ()) } /// Returns the calendar day value. pub fn day(&self) -> TemporalResult { - self.contextual_day(&mut ()) - } - - /// Returns the calendar day of week value with provided context. - pub fn contextual_day_of_week(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.day_of_week( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + self.calendar + .day(&CalendarDateLike::Date(self.clone()), &mut ()) } /// Returns the calendar day of week value. pub fn day_of_week(&self) -> TemporalResult { - self.contextual_day_of_week(&mut ()) - } - - /// Returns the calendar day of year value with provided context. - pub fn contextual_day_of_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.day_of_year( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + self.calendar + .day_of_week(&CalendarDateLike::Date(self.clone()), &mut ()) } /// Returns the calendar day of year value. pub fn day_of_year(&self) -> TemporalResult { - self.contextual_day_of_year(&mut ()) - } - - /// Returns the calendar week of year value with provided context. - pub fn contextual_week_of_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.week_of_year( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + self.calendar + .day_of_year(&CalendarDateLike::Date(self.clone()), &mut ()) } /// Returns the calendar week of year value. pub fn week_of_year(&self) -> TemporalResult { - self.contextual_week_of_year(&mut ()) - } - - /// Returns the calendar year of week value with provided context. - pub fn contextual_year_of_week(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.year_of_week( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + self.calendar + .week_of_year(&CalendarDateLike::Date(self.clone()), &mut ()) } /// Returns the calendar year of week value. pub fn year_of_week(&self) -> TemporalResult { - self.contextual_year_of_week(&mut ()) - } - - /// Returns the calendar days in week value with provided context. - pub fn contextual_days_in_week(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.days_in_week( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + self.calendar + .year_of_week(&CalendarDateLike::Date(self.clone()), &mut ()) } /// Returns the calendar days in week value. pub fn days_in_week(&self) -> TemporalResult { - self.contextual_days_in_week(&mut ()) - } - - /// Returns the calendar days in month value with provided context. - pub fn contextual_days_in_month(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.days_in_month( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + self.calendar + .days_in_week(&CalendarDateLike::Date(self.clone()), &mut ()) } /// Returns the calendar days in month value. pub fn days_in_month(&self) -> TemporalResult { - self.contextual_days_in_month(&mut ()) - } - - /// Returns the calendar days in year value with provided context. - pub fn contextual_days_in_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.days_in_year( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + self.calendar + .days_in_month(&CalendarDateLike::Date(self.clone()), &mut ()) } /// Returns the calendar days in year value. pub fn days_in_year(&self) -> TemporalResult { - self.contextual_days_in_year(&mut ()) - } - - /// Returns the calendar months in year value with provided context. - pub fn contextual_months_in_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.months_in_year( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + self.calendar + .days_in_year(&CalendarDateLike::Date(self.clone()), &mut ()) } /// Returns the calendar months in year value. pub fn months_in_year(&self) -> TemporalResult { - self.contextual_months_in_year(&mut ()) + self.calendar + .months_in_year(&CalendarDateLike::Date(self.clone()), &mut ()) + } + + /// Returns returns whether the date in a leap year for the given calendar. + pub fn in_leap_year(&self) -> TemporalResult { + self.calendar + .in_leap_year(&CalendarDateLike::Date(self.clone()), &mut ()) + } +} + +// NOTE(nekevss): The clone below should ideally not change the memory address, but that may +// not be true across all cases. I.e., it should be fine as long as the clone is simply a +// reference count increment. Need to test. +impl Date { + /// Returns the calendar year value with provided context. + pub fn contextualized_year( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .year(&CalendarDateLike::CustomDate(this.clone()), context) + } + + /// Returns the calendar month value with provided context. + pub fn contextualized_month( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .month(&CalendarDateLike::CustomDate(this.clone()), context) + } + + /// Returns the calendar month code value with provided context. + pub fn contextualized_month_code( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult> { + this.get_calendar() + .month_code(&CalendarDateLike::CustomDate(this.clone()), context) + } + + /// Returns the calendar day value with provided context. + pub fn contextualized_day( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .day(&CalendarDateLike::CustomDate(this.clone()), context) + } + + /// Returns the calendar day of week value with provided context. + pub fn contextualized_day_of_week( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .day_of_week(&CalendarDateLike::CustomDate(this.clone()), context) + } + + /// Returns the calendar day of year value with provided context. + pub fn contextualized_day_of_year( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .day_of_year(&CalendarDateLike::CustomDate(this.clone()), context) + } + + /// Returns the calendar week of year value with provided context. + pub fn contextualized_week_of_year( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .week_of_year(&CalendarDateLike::CustomDate(this.clone()), context) + } + + /// Returns the calendar year of week value with provided context. + pub fn contextualized_year_of_week( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .year_of_week(&CalendarDateLike::CustomDate(this.clone()), context) + } + + /// Returns the calendar days in week value with provided context. + pub fn contextualized_days_in_week( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .days_in_week(&CalendarDateLike::CustomDate(this.clone()), context) + } + + /// Returns the calendar days in month value with provided context. + pub fn contextualized_days_in_month( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .days_in_month(&CalendarDateLike::CustomDate(this.clone()), context) + } + + /// Returns the calendar days in year value with provided context. + pub fn contextualized_days_in_year( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .days_in_year(&CalendarDateLike::CustomDate(this.clone()), context) + } + + /// Returns the calendar months in year value with provided context. + pub fn contextualized_months_in_year( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .months_in_year(&CalendarDateLike::CustomDate(this.clone()), context) } /// Returns whether the date is in a leap year for the given calendar with provided context. - pub fn contextual_in_leap_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.in_leap_year( - &super::calendar::CalendarDateLike::Date(self.clone()), - context, - ) + pub fn contextualized_in_leap_year( + this: &>::Date, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .in_leap_year(&CalendarDateLike::CustomDate(this.clone()), context) } +} - /// Returns returns whether the date in a leap year for the given calendar. - pub fn in_leap_year(&self) -> TemporalResult { - self.contextual_in_leap_year(&mut ()) +impl GetCalendarSlot for Date { + fn get_calendar(&self) -> CalendarSlot { + self.calendar.clone() } } diff --git a/core/temporal/src/components/datetime.rs b/core/temporal/src/components/datetime.rs index 12cdcdd62e..1dfc69f3f9 100644 --- a/core/temporal/src/components/datetime.rs +++ b/core/temporal/src/components/datetime.rs @@ -14,6 +14,8 @@ use crate::{ use std::{any::Any, str::FromStr}; use tinystr::TinyAsciiStr; +use super::calendar::{CalendarDateLike, DateTypes, GetCalendarSlot}; + /// The native Rust implementation of `Temporal.PlainDateTime` #[derive(Debug, Default, Clone)] pub struct DateTime { @@ -48,13 +50,6 @@ impl DateTime { let iso = IsoDateTime::from_epoch_nanos(&instant.nanos, offset)?; Ok(Self { iso, calendar }) } - - /// Returns the inner `IsoDate` value. - #[inline] - #[must_use] - pub(crate) fn iso_date(&self) -> &IsoDate { - self.iso.date() - } } // ==== Public DateTime API ==== @@ -170,179 +165,220 @@ impl DateTime { // ==== Calendar-derived public API ==== -impl DateTime { - /// Returns the calendar year value with provided context. - pub fn contextual_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.year( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) - } - +// TODO: Revert to `DateTime`. +impl DateTime<()> { /// Returns the calendar year value. pub fn year(&self) -> TemporalResult { - self.contextual_year(&mut ()) - } - - /// Returns the calendar month value with provided context. - pub fn contextual_month(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.month( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .year(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns the calendar month value. pub fn month(&self) -> TemporalResult { - self.contextual_month(&mut ()) - } - - /// Returns the calendar month code value with provided context. - pub fn contextual_month_code(&self, context: &mut dyn Any) -> TemporalResult> { - self.calendar.month_code( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .month(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns the calendar month code value. pub fn month_code(&self) -> TemporalResult> { - self.contextual_month_code(&mut ()) - } - - /// Returns the calendar day value with provided context. - pub fn contextual_day(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.day( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .month_code(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns the calendar day value. pub fn day(&self) -> TemporalResult { - self.contextual_day(&mut ()) - } - - /// Returns the calendar day of week value with provided context. - pub fn contextual_day_of_week(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.day_of_week( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .day(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns the calendar day of week value. pub fn day_of_week(&self) -> TemporalResult { - self.contextual_day_of_week(&mut ()) - } - - /// Returns the calendar day of year value with provided context. - pub fn contextual_day_of_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.day_of_year( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .day_of_week(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns the calendar day of year value. pub fn day_of_year(&self) -> TemporalResult { - self.contextual_day_of_year(&mut ()) - } - - /// Returns the calendar week of year value with provided context. - pub fn contextual_week_of_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.week_of_year( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .day_of_year(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns the calendar week of year value. pub fn week_of_year(&self) -> TemporalResult { - self.contextual_week_of_year(&mut ()) - } - - /// Returns the calendar year of week value with provided context. - pub fn contextual_year_of_week(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.year_of_week( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .week_of_year(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns the calendar year of week value. pub fn year_of_week(&self) -> TemporalResult { - self.contextual_year_of_week(&mut ()) - } - - /// Returns the calendar days in week value with provided context. - pub fn contextual_days_in_week(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.days_in_week( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .year_of_week(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns the calendar days in week value. pub fn days_in_week(&self) -> TemporalResult { - self.contextual_days_in_week(&mut ()) - } - - /// Returns the calendar days in month value with provided context. - pub fn contextual_days_in_month(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.days_in_month( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .days_in_week(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns the calendar days in month value. pub fn days_in_month(&self) -> TemporalResult { - self.contextual_days_in_month(&mut ()) - } - - /// Returns the calendar days in year value with provided context. - pub fn contextual_days_in_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.days_in_year( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .days_in_month(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns the calendar days in year value. pub fn days_in_year(&self) -> TemporalResult { - self.contextual_days_in_year(&mut ()) - } - - /// Returns the calendar months in year value with provided context. - pub fn contextual_months_in_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.months_in_year( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .days_in_year(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns the calendar months in year value. pub fn months_in_year(&self) -> TemporalResult { - self.contextual_months_in_year(&mut ()) - } - - /// Returns whether the date is in a leap year for the given calendar with provided context. - pub fn contextual_in_leap_year(&self, context: &mut dyn Any) -> TemporalResult { - self.calendar.in_leap_year( - &super::calendar::CalendarDateLike::DateTime(self.clone()), - context, - ) + self.calendar + .months_in_year(&CalendarDateLike::DateTime(self.clone()), &mut ()) } /// Returns returns whether the date in a leap year for the given calendar. pub fn in_leap_year(&self) -> TemporalResult { - self.contextual_in_leap_year(&mut ()) + self.calendar + .in_leap_year(&CalendarDateLike::DateTime(self.clone()), &mut ()) + } +} + +impl DateTime { + /// Returns the calendar year value with provided context. + pub fn contextualized_year( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .year(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns the calendar month value with provided context. + pub fn contextualized_month( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .month(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns the calendar month code value with provided context. + pub fn contextualized_month_code( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult> { + this.get_calendar() + .month_code(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns the calendar day value with provided context. + pub fn contextualized_day( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .day(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns the calendar day of week value with provided context. + pub fn contextualized_day_of_week( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .day_of_week(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns the calendar day of year value with provided context. + pub fn contextualized_day_of_year( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .day_of_year(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns the calendar week of year value with provided context. + pub fn contextualized_week_of_year( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .week_of_year(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns the calendar year of week value with provided context. + pub fn contextualized_year_of_week( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .year_of_week(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns the calendar days in week value with provided context. + pub fn contextualized_days_in_week( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .days_in_week(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns the calendar days in month value with provided context. + pub fn contextualized_days_in_month( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .days_in_month(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns the calendar days in year value with provided context. + pub fn contextualized_days_in_year( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .days_in_year(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns the calendar months in year value with provided context. + pub fn contextualized_months_in_year( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .months_in_year(&CalendarDateLike::CustomDateTime(this.clone()), context) + } + + /// Returns whether the date is in a leap year for the given calendar with provided context. + pub fn contextualized_in_leap_year( + this: &>::DateTime, + context: &mut dyn Any, + ) -> TemporalResult { + this.get_calendar() + .in_leap_year(&CalendarDateLike::CustomDateTime(this.clone()), context) } } // ==== Trait impls ==== +impl GetCalendarSlot for DateTime { + fn get_calendar(&self) -> CalendarSlot { + self.calendar.clone() + } +} + +impl IsoDateSlots for DateTime { + fn iso_date(&self) -> IsoDate { + *self.iso.date() + } +} + impl FromStr for DateTime { type Err = TemporalError; diff --git a/core/temporal/src/components/month_day.rs b/core/temporal/src/components/month_day.rs index b31ec1391d..92a64de000 100644 --- a/core/temporal/src/components/month_day.rs +++ b/core/temporal/src/components/month_day.rs @@ -9,7 +9,7 @@ use crate::{ TemporalError, TemporalResult, }; -use super::calendar::CalendarProtocol; +use super::calendar::{CalendarProtocol, GetCalendarSlot}; /// The native Rust implementation of `Temporal.PlainMonthDay` #[derive(Debug, Default, Clone)] @@ -60,6 +60,12 @@ impl MonthDay { } } +impl GetCalendarSlot for MonthDay { + fn get_calendar(&self) -> CalendarSlot { + self.calendar.clone() + } +} + impl IsoDateSlots for MonthDay { #[inline] /// Returns this structs `IsoDate`. diff --git a/core/temporal/src/components/year_month.rs b/core/temporal/src/components/year_month.rs index 0cb7f4f0a7..9686c784ea 100644 --- a/core/temporal/src/components/year_month.rs +++ b/core/temporal/src/components/year_month.rs @@ -9,7 +9,7 @@ use crate::{ TemporalError, TemporalResult, }; -use super::calendar::CalendarProtocol; +use super::calendar::{CalendarProtocol, GetCalendarSlot}; /// The native Rust implementation of `Temporal.YearMonth`. #[derive(Debug, Default, Clone)] @@ -54,14 +54,21 @@ impl YearMonth { self.iso.month } + /// Returns the Calendar value. #[inline] #[must_use] - /// Returns a reference to `YearMonth`'s `CalendarSlot` pub fn calendar(&self) -> &CalendarSlot { &self.calendar } } +impl GetCalendarSlot for YearMonth { + /// Returns a reference to `YearMonth`'s `CalendarSlot` + fn get_calendar(&self) -> CalendarSlot { + self.calendar.clone() + } +} + impl IsoDateSlots for YearMonth { #[inline] /// Returns this `YearMonth`'s `IsoDate`