From 42e56848fb8e2314978e13f74b67f0f633516c6e Mon Sep 17 00:00:00 2001 From: Kevin Ness Date: Thu, 10 Jul 2025 21:09:47 -0500 Subject: [PATCH 1/4] Build out structs of resolved options --- src/options.rs | 3 + src/options/resolved.rs | 168 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/options/resolved.rs diff --git a/src/options.rs b/src/options.rs index f926db791..ab5b2e47c 100644 --- a/src/options.rs +++ b/src/options.rs @@ -10,6 +10,9 @@ use core::{fmt, str::FromStr}; mod increment; mod relative_to; +mod resolved; + +pub use resolved::*; pub use increment::RoundingIncrement; pub use relative_to::RelativeTo; diff --git a/src/options/resolved.rs b/src/options/resolved.rs new file mode 100644 index 000000000..57fdf35d8 --- /dev/null +++ b/src/options/resolved.rs @@ -0,0 +1,168 @@ +//! A module for all resolved option types + +use crate::options::{RoundingIncrement, RoundingMode}; + +pub struct ResolvedDurationRoundOptions { + pub largest_unit: ResolvedDurationLargestUnit, + pub smallest_unit: ResolvedDurationSmallestUnit, + pub rounding_mode: RoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedDurationLargestUnit(ResolvedUnit); + +pub struct ResolvedDurationSmallestUnit(ResolvedUnit); + +// ==== PlainDate variation ==== + +pub struct ResolvedPlainDateRoundOptions { + pub largest_unit: ResolvedDurationLargestUnit, + pub smallest_unit: ResolvedDurationSmallestUnit, + pub rounding_mode: RoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedPlainDateUntilDifferenceSettings { + pub largest_unit: ResolvedPlainDateLargestUnit, + pub smallest_unit: ResolvedPlainDateSmallestUnit, + pub rounding_mode: RoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedPlainDateSinceDifferenceSettings { + pub largest_unit: ResolvedPlainDateLargestUnit, + pub smallest_unit: ResolvedPlainDateSmallestUnit, + pub rounding_mode: NegatedRoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedPlainDateLargestUnit(ResolvedUnit); + +pub struct ResolvedPlainDateSmallestUnit(ResolvedUnit); + +// ==== PlainDateTime variation ==== + +pub struct ResolvedPlainDateTimeRoundOptions { + pub largest_unit: ResolvedPlainDateLargestUnit, + pub smallest_unit: ResolvedPlainDateTimeSmallestUnit, + pub rounding_mode: RoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedPlainDateTimeUntilDifferenceSettings { + pub largest_unit: ResolvedPlainDateTimeLargestUnit, + pub smallest_unit: ResolvedPlainDateTimeSmallestUnit, + pub rounding_mode: RoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedPlainDateTimeSinceDifferenceSettings { + pub largest_unit: ResolvedPlainDateLargestUnit, + pub smallest_unit: ResolvedPlainDateLargestUnit, + pub rounding_mode: NegatedRoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedPlainDateTimeLargestUnit(ResolvedUnit); + +pub struct ResolvedPlainDateTimeSmallestUnit(ResolvedUnit); + +// ==== ZonedDateTime variation ==== + +pub struct ResolvedZonedDateTimeRoundOptions { + pub largest_unit: ResolvedPlainDateTimeLargestUnit, + pub smallest_unit: ResolvedPlainDateTimeSmallestUnit, + pub rounding_mode: RoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedZonedDateTimeUntilDifferenceSettings { + pub largest_unit: ResolvedPlainDateTimeLargestUnit, + pub smallest_unit: ResolvedPlainDateTimeSmallestUnit, + pub rounding_mode: RoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedZonedDateTimeSinceDifferenceSettings { + pub largest_unit: ResolvedPlainDateTimeLargestUnit, + pub smallest_unit: ResolvedPlainDateTimeSmallestUnit, + pub rounding_mode: NegatedRoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedZonedDateTimeLargestUnit(ResolvedUnit); + +pub struct ResolvedZonedDateTimeSmallestUnit(ResolvedUnit); + +// ==== PlainTime variation ==== + +pub struct ResolvedTimeRoundOptions { + pub largest_unit: ResolvedTimeLargestUnit, + pub smallest_unit: ResolvedTimeSmallestUnit, + pub rounding_mode: RoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedTimeUntilDifferenceSettings { + pub largest_unit: ResolvedTimeLargestUnit, + pub smallest_unit: ResolvedTimeSmallestUnit, + pub rounding_mode: RoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedTimeSinceDifferenceSettings { + pub largest_unit: ResolvedTimeLargestUnit, + pub smallest_unit: ResolvedTimeSmallestUnit, + pub rounding_mode: RoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedTimeLargestUnit(ResolvedUnit); + +pub struct ResolvedTimeSmallestUnit(ResolvedUnit); + +// ==== YearMonth variation ==== + +pub struct ResolvedYearMonthUntilDifferenceSettings { + pub largest_unit: ResolvedZonedDateTimeLargestUnit, + pub smallest_unit: ResolvedZonedDateTimeSmallestUnit, + pub rounding_mode: RoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedYearMonthSinceDifferenceSettings { + pub largest_unit: ResolvedZonedDateTimeLargestUnit, + pub smallest_unit: ResolvedZonedDateTimeSmallestUnit, + pub rounding_mode: NegatedRoundingMode, + pub increment: RoundingIncrement, +} + +pub struct ResolvedYearMonthLargestUnit(ResolvedUnit); + +pub struct ResolvedYearMonthSmallestUnit(ResolvedUnit); + +pub struct NegatedRoundingMode(RoundingMode); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum ResolvedUnit { + /// The `Nanosecond` unit + Nanosecond, + /// The `Microsecond` unit + Microsecond, + /// The `Millisecond` unit + Millisecond, + /// The `Second` unit + Second, + /// The `Minute` unit + Minute, + /// The `Hour` unit + Hour, + /// The `Day` unit + Day, + /// The `Week` unit + Week, + /// The `Month` unit + Month, + /// The `Year` unit + Year, +} From 90ec7c5596601c0ef5aa8070cbd1b8055eabd052 Mon Sep 17 00:00:00 2001 From: Kevin Ness Date: Thu, 10 Jul 2025 21:39:38 -0500 Subject: [PATCH 2/4] Add some constructors + remove some unneccessary structs --- src/options/resolved.rs | 75 +++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/src/options/resolved.rs b/src/options/resolved.rs index 57fdf35d8..0c39d4570 100644 --- a/src/options/resolved.rs +++ b/src/options/resolved.rs @@ -1,27 +1,21 @@ //! A module for all resolved option types -use crate::options::{RoundingIncrement, RoundingMode}; +use core::{fmt, str::FromStr}; + +use crate::{ + options::{RoundingIncrement, RoundingMode, Unit}, + TemporalResult, +}; pub struct ResolvedDurationRoundOptions { - pub largest_unit: ResolvedDurationLargestUnit, - pub smallest_unit: ResolvedDurationSmallestUnit, + pub largest_unit: ResolvedUnit, + pub smallest_unit: ResolvedUnit, pub rounding_mode: RoundingMode, pub increment: RoundingIncrement, } -pub struct ResolvedDurationLargestUnit(ResolvedUnit); - -pub struct ResolvedDurationSmallestUnit(ResolvedUnit); - // ==== PlainDate variation ==== -pub struct ResolvedPlainDateRoundOptions { - pub largest_unit: ResolvedDurationLargestUnit, - pub smallest_unit: ResolvedDurationSmallestUnit, - pub rounding_mode: RoundingMode, - pub increment: RoundingIncrement, -} - pub struct ResolvedPlainDateUntilDifferenceSettings { pub largest_unit: ResolvedPlainDateLargestUnit, pub smallest_unit: ResolvedPlainDateSmallestUnit, @@ -143,6 +137,12 @@ pub struct ResolvedYearMonthSmallestUnit(ResolvedUnit); pub struct NegatedRoundingMode(RoundingMode); +impl From for NegatedRoundingMode { + fn from(value: RoundingMode) -> Self { + NegatedRoundingMode(value.negate()) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum ResolvedUnit { /// The `Nanosecond` unit @@ -166,3 +166,50 @@ pub enum ResolvedUnit { /// The `Year` unit Year, } +/// A parsing error for `Unit` +#[derive(Debug, Clone, Copy)] +pub struct ParseResolvedUnitError; + +impl fmt::Display for ParseResolvedUnitError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("provided string was not a valid Unit") + } +} + +impl FromStr for ResolvedUnit { + type Err = ParseResolvedUnitError; + + fn from_str(s: &str) -> Result { + match s { + "year" | "years" => Ok(Self::Year), + "month" | "months" => Ok(Self::Month), + "week" | "weeks" => Ok(Self::Week), + "day" | "days" => Ok(Self::Day), + "hour" | "hours" => Ok(Self::Hour), + "minute" | "minutes" => Ok(Self::Minute), + "second" | "seconds" => Ok(Self::Second), + "millisecond" | "milliseconds" => Ok(Self::Millisecond), + "microsecond" | "microseconds" => Ok(Self::Microsecond), + "nanosecond" | "nanoseconds" => Ok(Self::Nanosecond), + _ => Err(ParseResolvedUnitError), + } + } +} + +impl fmt::Display for ResolvedUnit { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Year => "year", + Self::Month => "month", + Self::Week => "week", + Self::Day => "day", + Self::Hour => "hour", + Self::Minute => "minute", + Self::Second => "second", + Self::Millisecond => "millsecond", + Self::Microsecond => "microsecond", + Self::Nanosecond => "nanosecond", + } + .fmt(f) + } +} From a42eaf08ea665dff70482221d380ca41bbfbbb67 Mon Sep 17 00:00:00 2001 From: nekevss Date: Sun, 13 Jul 2025 16:56:12 -0500 Subject: [PATCH 3/4] Add general constructors and validation logic --- src/options/resolved.rs | 375 +++++++++++++++++++++++++++++++++------- 1 file changed, 312 insertions(+), 63 deletions(-) diff --git a/src/options/resolved.rs b/src/options/resolved.rs index 0c39d4570..d0d341efb 100644 --- a/src/options/resolved.rs +++ b/src/options/resolved.rs @@ -1,10 +1,10 @@ //! A module for all resolved option types use core::{fmt, str::FromStr}; +use alloc::string::ToString; use crate::{ - options::{RoundingIncrement, RoundingMode, Unit}, - TemporalResult, + options::{RoundingIncrement, RoundingMode, Unit}, TemporalError, TemporalResult }; pub struct ResolvedDurationRoundOptions { @@ -26,14 +26,80 @@ pub struct ResolvedPlainDateUntilDifferenceSettings { pub struct ResolvedPlainDateSinceDifferenceSettings { pub largest_unit: ResolvedPlainDateLargestUnit, pub smallest_unit: ResolvedPlainDateSmallestUnit, - pub rounding_mode: NegatedRoundingMode, + pub negated_rounding_mode: NegatedRoundingMode, pub increment: RoundingIncrement, } +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] pub struct ResolvedPlainDateLargestUnit(ResolvedUnit); +impl ResolvedPlainDateLargestUnit { + pub fn try_from_units(unit: DateUnitOrAuto, resolved_smallest_unit: ResolvedPlainDateSmallestUnit) -> TemporalResult { + // NOTE: Types enforce no auto invariant. + // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). + let default_largest = Unit::larger(Unit::Day, resolved_smallest_unit.0.0).expect("no auto"); + + // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. + let largest_unit = if unit.0 == Unit::Auto { + ResolvedUnit::try_from_unit(default_largest)? + } else { + ResolvedUnit::try_from_unit(unit.0)? + }; + + // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. + if ResolvedUnit::larger(largest_unit, resolved_smallest_unit.0) != largest_unit { + return Err(TemporalError::range()) + } + Ok(Self(largest_unit)) + } +} + +const DATE_UNITS:[Unit; 4] = [Unit::Year, Unit::Month, Unit::Week, Unit::Day]; + +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] pub struct ResolvedPlainDateSmallestUnit(ResolvedUnit); +impl ResolvedPlainDateSmallestUnit { + pub fn try_from_unit(unit: Unit) -> TemporalResult { + if !DATE_UNITS.contains(&unit) { + // TODO: RangeError message. + return Err(TemporalError::range()) + } + Ok(Self(ResolvedUnit(unit))) + } + + pub fn to_maximum_rounding_increment(&self) -> Option { + self.0.0.to_maximum_rounding_increment() + } +} + +#[derive(Debug, Clone, Copy)] +// Required for Plain Date largest unit resolution +#[repr(transparent)] +pub struct DateUnitOrAuto(Unit); + +impl DateUnitOrAuto { + pub fn try_from_unit(unit: Unit) -> TemporalResult { + if !DATE_UNITS.contains(&unit) && unit != Unit::Auto { + // TODO: RangeError message. + return Err(TemporalError::range()) + } + Ok(Self(unit)) + } +} + +impl FromStr for DateUnitOrAuto { + type Err = TemporalError; + + fn from_str(s: &str) -> Result { + let unit = Unit::from_str(s) + .map_err(|e| TemporalError::range().with_message(e.to_string()))?; + Self::try_from_unit(unit) + } +} + // ==== PlainDateTime variation ==== pub struct ResolvedPlainDateTimeRoundOptions { @@ -53,14 +119,46 @@ pub struct ResolvedPlainDateTimeUntilDifferenceSettings { pub struct ResolvedPlainDateTimeSinceDifferenceSettings { pub largest_unit: ResolvedPlainDateLargestUnit, pub smallest_unit: ResolvedPlainDateLargestUnit, - pub rounding_mode: NegatedRoundingMode, + pub negated_rounding_mode: NegatedRoundingMode, pub increment: RoundingIncrement, } +#[derive(Debug, Clone, Copy)] pub struct ResolvedPlainDateTimeLargestUnit(ResolvedUnit); +impl ResolvedPlainDateTimeLargestUnit { + pub fn try_from_units(unit: Unit, resolved_smallest_unit: ResolvedPlainDateTimeSmallestUnit) -> TemporalResult { + // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). + let default_largest = ResolvedUnit::larger(ResolvedUnit(Unit::Day), resolved_smallest_unit.0); + + // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. + let largest_unit = if unit == Unit::Auto { + default_largest + } else { + ResolvedUnit::try_from_unit(unit)? + }; + + // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. + if ResolvedUnit::larger(largest_unit, resolved_smallest_unit.0) != largest_unit { + return Err(TemporalError::range()) + } + Ok(Self(largest_unit)) + } +} + +#[derive(Debug, Clone, Copy)] pub struct ResolvedPlainDateTimeSmallestUnit(ResolvedUnit); +impl ResolvedPlainDateTimeSmallestUnit { + pub fn try_from_unit(unit: Unit) -> TemporalResult { + if unit != Unit::Auto { + // TODO: RangeError message. + return Err(TemporalError::range()) + } + Ok(Self(ResolvedUnit(unit))) + } +} + // ==== ZonedDateTime variation ==== pub struct ResolvedZonedDateTimeRoundOptions { @@ -80,19 +178,51 @@ pub struct ResolvedZonedDateTimeUntilDifferenceSettings { pub struct ResolvedZonedDateTimeSinceDifferenceSettings { pub largest_unit: ResolvedPlainDateTimeLargestUnit, pub smallest_unit: ResolvedPlainDateTimeSmallestUnit, - pub rounding_mode: NegatedRoundingMode, + pub negated_rounding_mode: NegatedRoundingMode, pub increment: RoundingIncrement, } +#[derive(Debug, Clone, Copy)] pub struct ResolvedZonedDateTimeLargestUnit(ResolvedUnit); +impl ResolvedZonedDateTimeLargestUnit { + pub fn try_from_units(unit: Unit, resolved_smallest_unit: ResolvedPlainDateTimeSmallestUnit) -> TemporalResult { + // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). + let default_largest = ResolvedUnit::larger(ResolvedUnit(Unit::Hour), resolved_smallest_unit.0); + + // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. + let largest_unit = if unit == Unit::Auto { + default_largest + } else { + ResolvedUnit::try_from_unit(unit)? + }; + + // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. + if ResolvedUnit::larger(largest_unit, resolved_smallest_unit.0) != largest_unit { + return Err(TemporalError::range()) + } + Ok(Self(largest_unit)) + } +} + +#[derive(Debug, Clone, Copy)] pub struct ResolvedZonedDateTimeSmallestUnit(ResolvedUnit); +impl ResolvedZonedDateTimeSmallestUnit { + pub fn try_from_unit(unit: Unit) -> TemporalResult { + if unit != Unit::Auto { + // TODO: RangeError message. + return Err(TemporalError::range()) + } + Ok(Self(ResolvedUnit(unit))) + } +} + // ==== PlainTime variation ==== pub struct ResolvedTimeRoundOptions { - pub largest_unit: ResolvedTimeLargestUnit, - pub smallest_unit: ResolvedTimeSmallestUnit, + pub largest_unit: ResolvedTimeLargestUnit, // Potentially need different types or methods + pub smallest_unit: ResolvedTimeSmallestUnit, // Potentially need different types or methods pub rounding_mode: RoundingMode, pub increment: RoundingIncrement, } @@ -111,10 +241,64 @@ pub struct ResolvedTimeSinceDifferenceSettings { pub increment: RoundingIncrement, } +const TIME_UNITS: [Unit; 6] = [ + Unit::Hour, + Unit::Minute, + Unit::Second, + Unit::Millisecond, + Unit::Microsecond, + Unit::Nanosecond, +]; + +#[derive(Debug, Clone, Copy)] pub struct ResolvedTimeLargestUnit(ResolvedUnit); +impl ResolvedTimeLargestUnit { + pub fn try_from_units(unit: Unit, resolved_smallest_unit: ResolvedPlainDateTimeSmallestUnit) -> TemporalResult { + // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). + let default_largest = ResolvedUnit::larger(ResolvedUnit(Unit::Hour), resolved_smallest_unit.0); + + // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. + let largest_unit = if unit == Unit::Auto { + default_largest + } else { + ResolvedUnit::try_from_unit(unit)? + }; + + // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. + if ResolvedUnit::larger(largest_unit, resolved_smallest_unit.0) != largest_unit { + return Err(TemporalError::range()) + } + Ok(Self(largest_unit)) + } +} + +#[derive(Debug, Clone, Copy)] pub struct ResolvedTimeSmallestUnit(ResolvedUnit); +impl ResolvedTimeSmallestUnit { + pub fn try_from_unit(unit: Unit) -> TemporalResult { + if !TIME_UNITS.contains(&unit) { + // TODO: RangeError message. + return Err(TemporalError::range()) + } + Ok(Self(ResolvedUnit(unit))) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct TimeUnitOrAuto(Unit); + +impl TimeUnitOrAuto { + pub fn try_from_unit(unit: Unit) -> TemporalResult { + if !TIME_UNITS.contains(&unit) && unit != Unit::Auto { + // TODO: RangeError message. + return Err(TemporalError::range()) + } + Ok(Self(unit)) + } +} + // ==== YearMonth variation ==== pub struct ResolvedYearMonthUntilDifferenceSettings { @@ -127,14 +311,51 @@ pub struct ResolvedYearMonthUntilDifferenceSettings { pub struct ResolvedYearMonthSinceDifferenceSettings { pub largest_unit: ResolvedZonedDateTimeLargestUnit, pub smallest_unit: ResolvedZonedDateTimeSmallestUnit, - pub rounding_mode: NegatedRoundingMode, + pub negated_rounding_mode: NegatedRoundingMode, pub increment: RoundingIncrement, } +#[derive(Debug, Clone, Copy)] pub struct ResolvedYearMonthLargestUnit(ResolvedUnit); +impl ResolvedYearMonthLargestUnit { + pub fn try_from_units(unit: YearMonthUnitOrAuto, resolved_smallest_unit: ResolvedYearMonthSmallestUnit) -> TemporalResult { + // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). + let default_largest = ResolvedUnit::larger(ResolvedUnit(Unit::Hour), resolved_smallest_unit.0); + + // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. + let largest_unit = if unit.0 == Unit::Auto { + default_largest + } else { + ResolvedUnit::try_from_unit(unit.0)? + }; + + // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. + if ResolvedUnit::larger(largest_unit, resolved_smallest_unit.0) != largest_unit { + return Err(TemporalError::range()) + } + Ok(Self(largest_unit)) + } +} + +#[derive(Debug, Clone, Copy)] pub struct ResolvedYearMonthSmallestUnit(ResolvedUnit); +#[derive(Debug, Clone, Copy)] +pub struct YearMonthUnitOrAuto(Unit); + +impl YearMonthUnitOrAuto { + pub fn try_from_unit(unit: Unit) -> TemporalResult { + if ![Unit::Year, Unit::Month].contains(&unit) && unit != Unit::Auto { + // TODO: RangeError message. + return Err(TemporalError::range()) + } + Ok(Self(unit)) + } +} + +// ==== Extra options ==== + pub struct NegatedRoundingMode(RoundingMode); impl From for NegatedRoundingMode { @@ -144,72 +365,100 @@ impl From for NegatedRoundingMode { } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum ResolvedUnit { - /// The `Nanosecond` unit - Nanosecond, - /// The `Microsecond` unit - Microsecond, - /// The `Millisecond` unit - Millisecond, - /// The `Second` unit - Second, - /// The `Minute` unit - Minute, - /// The `Hour` unit - Hour, - /// The `Day` unit - Day, - /// The `Week` unit - Week, - /// The `Month` unit - Month, - /// The `Year` unit - Year, -} -/// A parsing error for `Unit` -#[derive(Debug, Clone, Copy)] -pub struct ParseResolvedUnitError; +#[repr(transparent)] +pub struct ResolvedUnit(Unit); + +impl ResolvedUnit { + fn try_from_unit(unit: Unit) -> TemporalResult { + if unit == Unit::Auto { + return Err(TemporalError::range()) + } + Ok(Self(unit)) + } + + fn larger(u1: ResolvedUnit, u2: ResolvedUnit) -> ResolvedUnit { + Self(Unit::larger(u1.0, u2.0).expect("no auto")) -impl fmt::Display for ParseResolvedUnitError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str("provided string was not a valid Unit") } } impl FromStr for ResolvedUnit { - type Err = ParseResolvedUnitError; + type Err = TemporalError; fn from_str(s: &str) -> Result { - match s { - "year" | "years" => Ok(Self::Year), - "month" | "months" => Ok(Self::Month), - "week" | "weeks" => Ok(Self::Week), - "day" | "days" => Ok(Self::Day), - "hour" | "hours" => Ok(Self::Hour), - "minute" | "minutes" => Ok(Self::Minute), - "second" | "seconds" => Ok(Self::Second), - "millisecond" | "milliseconds" => Ok(Self::Millisecond), - "microsecond" | "microseconds" => Ok(Self::Microsecond), - "nanosecond" | "nanoseconds" => Ok(Self::Nanosecond), - _ => Err(ParseResolvedUnitError), - } + let unit = Unit::from_str(s).map_err(|_| TemporalError::range())?; + Self::try_from_unit(unit) } } impl fmt::Display for ResolvedUnit { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Year => "year", - Self::Month => "month", - Self::Week => "week", - Self::Day => "day", - Self::Hour => "hour", - Self::Minute => "minute", - Self::Second => "second", - Self::Millisecond => "millsecond", - Self::Microsecond => "microsecond", - Self::Nanosecond => "nanosecond", - } - .fmt(f) + self.0.fmt(f) } } + + +#[cfg(test)] +mod tests { + use core::str::FromStr; + + use crate::options::{DateUnitOrAuto, NegatedRoundingMode, ResolvedPlainDateLargestUnit, ResolvedPlainDateSinceDifferenceSettings, ResolvedPlainDateSmallestUnit, ResolvedPlainDateUntilDifferenceSettings, RoundingIncrement, RoundingMode, Unit}; + + #[test] + fn impl_plain_date_get_difference_settings() { + struct JsOptions { + smallest_unit: &'static str, + largest_unit: &'static str, + increment: u32, + rounding_mode: &'static str, + } + + let js_options = JsOptions { + smallest_unit: "day", + largest_unit: "auto", + increment: 1, + rounding_mode: "floor", + }; + + // 1. NOTE: The following steps read options and perform independent validation in alphabetical order. + // 2. Let largestUnit be ? GetTemporalUnitValuedOption(options, "largestUnit", unitGroup, auto). + // 3. If disallowedUnits contains largestUnit, throw a RangeError exception. + let unit = Unit::from_str(js_options.largest_unit).unwrap(); + let largest_unit = DateUnitOrAuto::try_from_unit(unit).unwrap(); + // 4. Let roundingIncrement be ? GetRoundingIncrementOption(options). + let increment = RoundingIncrement::try_new(js_options.increment).unwrap(); + // 5. Let roundingMode be ? GetRoundingModeOption(options, trunc). + let rounding_mode = RoundingMode::from_str(js_options.rounding_mode).unwrap(); + // 6. If operation is since, then + // a. Set roundingMode to NegateRoundingMode(roundingMode). + let negated_rounding_mode = NegatedRoundingMode::from(rounding_mode); + // 7. Let smallestUnit be ? GetTemporalUnitValuedOption(options, "smallestUnit", unitGroup, fallbackSmallestUnit). + // 8. If disallowedUnits contains smallestUnit, throw a RangeError exception. + let unit = Unit::from_str(js_options.smallest_unit).unwrap(); + let smallest_unit = ResolvedPlainDateSmallestUnit::try_from_unit(unit).unwrap(); + // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). + // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. + // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. + let largest_unit = ResolvedPlainDateLargestUnit::try_from_units(largest_unit, smallest_unit).unwrap(); + // 12. Let maximum be MaximumTemporalDurationRoundingIncrement(smallestUnit). + let maximum = smallest_unit.to_maximum_rounding_increment(); + // 13. If maximum is not unset, perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false). + if let Some(max) = maximum { + increment.validate(max.into(), false).unwrap(); + } + // 14. Return the Record { [[SmallestUnit]]: smallestUnit, [[LargestUnit]]: largestUnit, [[RoundingMode]]: roundingMode, [[RoundingIncrement]]: roundingIncrement, }. + let _until_options = ResolvedPlainDateUntilDifferenceSettings { + largest_unit, + smallest_unit, + rounding_mode, + increment, + }; + + let _since_options = ResolvedPlainDateSinceDifferenceSettings { + largest_unit, + smallest_unit, + negated_rounding_mode, + increment, + }; + } +} \ No newline at end of file From 6a3b8bac14cf63dd359959e9ff34b4145f0bc296 Mon Sep 17 00:00:00 2001 From: nekevss Date: Sun, 13 Jul 2025 16:59:35 -0500 Subject: [PATCH 4/4] cargo fmt --- src/options/resolved.rs | 102 +++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/src/options/resolved.rs b/src/options/resolved.rs index d0d341efb..5969f5bd3 100644 --- a/src/options/resolved.rs +++ b/src/options/resolved.rs @@ -1,10 +1,11 @@ //! A module for all resolved option types -use core::{fmt, str::FromStr}; use alloc::string::ToString; +use core::{fmt, str::FromStr}; use crate::{ - options::{RoundingIncrement, RoundingMode, Unit}, TemporalError, TemporalResult + options::{RoundingIncrement, RoundingMode, Unit}, + TemporalError, TemporalResult, }; pub struct ResolvedDurationRoundOptions { @@ -35,10 +36,14 @@ pub struct ResolvedPlainDateSinceDifferenceSettings { pub struct ResolvedPlainDateLargestUnit(ResolvedUnit); impl ResolvedPlainDateLargestUnit { - pub fn try_from_units(unit: DateUnitOrAuto, resolved_smallest_unit: ResolvedPlainDateSmallestUnit) -> TemporalResult { + pub fn try_from_units( + unit: DateUnitOrAuto, + resolved_smallest_unit: ResolvedPlainDateSmallestUnit, + ) -> TemporalResult { // NOTE: Types enforce no auto invariant. // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). - let default_largest = Unit::larger(Unit::Day, resolved_smallest_unit.0.0).expect("no auto"); + let default_largest = + Unit::larger(Unit::Day, resolved_smallest_unit.0 .0).expect("no auto"); // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. let largest_unit = if unit.0 == Unit::Auto { @@ -46,16 +51,16 @@ impl ResolvedPlainDateLargestUnit { } else { ResolvedUnit::try_from_unit(unit.0)? }; - + // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. if ResolvedUnit::larger(largest_unit, resolved_smallest_unit.0) != largest_unit { - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(largest_unit)) } } -const DATE_UNITS:[Unit; 4] = [Unit::Year, Unit::Month, Unit::Week, Unit::Day]; +const DATE_UNITS: [Unit; 4] = [Unit::Year, Unit::Month, Unit::Week, Unit::Day]; #[derive(Debug, Clone, Copy)] #[repr(transparent)] @@ -65,13 +70,13 @@ impl ResolvedPlainDateSmallestUnit { pub fn try_from_unit(unit: Unit) -> TemporalResult { if !DATE_UNITS.contains(&unit) { // TODO: RangeError message. - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(ResolvedUnit(unit))) } pub fn to_maximum_rounding_increment(&self) -> Option { - self.0.0.to_maximum_rounding_increment() + self.0 .0.to_maximum_rounding_increment() } } @@ -84,7 +89,7 @@ impl DateUnitOrAuto { pub fn try_from_unit(unit: Unit) -> TemporalResult { if !DATE_UNITS.contains(&unit) && unit != Unit::Auto { // TODO: RangeError message. - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(unit)) } @@ -94,8 +99,8 @@ impl FromStr for DateUnitOrAuto { type Err = TemporalError; fn from_str(s: &str) -> Result { - let unit = Unit::from_str(s) - .map_err(|e| TemporalError::range().with_message(e.to_string()))?; + let unit = + Unit::from_str(s).map_err(|e| TemporalError::range().with_message(e.to_string()))?; Self::try_from_unit(unit) } } @@ -127,9 +132,13 @@ pub struct ResolvedPlainDateTimeSinceDifferenceSettings { pub struct ResolvedPlainDateTimeLargestUnit(ResolvedUnit); impl ResolvedPlainDateTimeLargestUnit { - pub fn try_from_units(unit: Unit, resolved_smallest_unit: ResolvedPlainDateTimeSmallestUnit) -> TemporalResult { + pub fn try_from_units( + unit: Unit, + resolved_smallest_unit: ResolvedPlainDateTimeSmallestUnit, + ) -> TemporalResult { // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). - let default_largest = ResolvedUnit::larger(ResolvedUnit(Unit::Day), resolved_smallest_unit.0); + let default_largest = + ResolvedUnit::larger(ResolvedUnit(Unit::Day), resolved_smallest_unit.0); // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. let largest_unit = if unit == Unit::Auto { @@ -137,10 +146,10 @@ impl ResolvedPlainDateTimeLargestUnit { } else { ResolvedUnit::try_from_unit(unit)? }; - + // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. if ResolvedUnit::larger(largest_unit, resolved_smallest_unit.0) != largest_unit { - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(largest_unit)) } @@ -153,7 +162,7 @@ impl ResolvedPlainDateTimeSmallestUnit { pub fn try_from_unit(unit: Unit) -> TemporalResult { if unit != Unit::Auto { // TODO: RangeError message. - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(ResolvedUnit(unit))) } @@ -186,9 +195,13 @@ pub struct ResolvedZonedDateTimeSinceDifferenceSettings { pub struct ResolvedZonedDateTimeLargestUnit(ResolvedUnit); impl ResolvedZonedDateTimeLargestUnit { - pub fn try_from_units(unit: Unit, resolved_smallest_unit: ResolvedPlainDateTimeSmallestUnit) -> TemporalResult { + pub fn try_from_units( + unit: Unit, + resolved_smallest_unit: ResolvedPlainDateTimeSmallestUnit, + ) -> TemporalResult { // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). - let default_largest = ResolvedUnit::larger(ResolvedUnit(Unit::Hour), resolved_smallest_unit.0); + let default_largest = + ResolvedUnit::larger(ResolvedUnit(Unit::Hour), resolved_smallest_unit.0); // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. let largest_unit = if unit == Unit::Auto { @@ -196,10 +209,10 @@ impl ResolvedZonedDateTimeLargestUnit { } else { ResolvedUnit::try_from_unit(unit)? }; - + // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. if ResolvedUnit::larger(largest_unit, resolved_smallest_unit.0) != largest_unit { - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(largest_unit)) } @@ -212,7 +225,7 @@ impl ResolvedZonedDateTimeSmallestUnit { pub fn try_from_unit(unit: Unit) -> TemporalResult { if unit != Unit::Auto { // TODO: RangeError message. - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(ResolvedUnit(unit))) } @@ -254,9 +267,13 @@ const TIME_UNITS: [Unit; 6] = [ pub struct ResolvedTimeLargestUnit(ResolvedUnit); impl ResolvedTimeLargestUnit { - pub fn try_from_units(unit: Unit, resolved_smallest_unit: ResolvedPlainDateTimeSmallestUnit) -> TemporalResult { + pub fn try_from_units( + unit: Unit, + resolved_smallest_unit: ResolvedPlainDateTimeSmallestUnit, + ) -> TemporalResult { // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). - let default_largest = ResolvedUnit::larger(ResolvedUnit(Unit::Hour), resolved_smallest_unit.0); + let default_largest = + ResolvedUnit::larger(ResolvedUnit(Unit::Hour), resolved_smallest_unit.0); // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. let largest_unit = if unit == Unit::Auto { @@ -264,10 +281,10 @@ impl ResolvedTimeLargestUnit { } else { ResolvedUnit::try_from_unit(unit)? }; - + // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. if ResolvedUnit::larger(largest_unit, resolved_smallest_unit.0) != largest_unit { - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(largest_unit)) } @@ -280,7 +297,7 @@ impl ResolvedTimeSmallestUnit { pub fn try_from_unit(unit: Unit) -> TemporalResult { if !TIME_UNITS.contains(&unit) { // TODO: RangeError message. - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(ResolvedUnit(unit))) } @@ -293,7 +310,7 @@ impl TimeUnitOrAuto { pub fn try_from_unit(unit: Unit) -> TemporalResult { if !TIME_UNITS.contains(&unit) && unit != Unit::Auto { // TODO: RangeError message. - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(unit)) } @@ -319,9 +336,13 @@ pub struct ResolvedYearMonthSinceDifferenceSettings { pub struct ResolvedYearMonthLargestUnit(ResolvedUnit); impl ResolvedYearMonthLargestUnit { - pub fn try_from_units(unit: YearMonthUnitOrAuto, resolved_smallest_unit: ResolvedYearMonthSmallestUnit) -> TemporalResult { + pub fn try_from_units( + unit: YearMonthUnitOrAuto, + resolved_smallest_unit: ResolvedYearMonthSmallestUnit, + ) -> TemporalResult { // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). - let default_largest = ResolvedUnit::larger(ResolvedUnit(Unit::Hour), resolved_smallest_unit.0); + let default_largest = + ResolvedUnit::larger(ResolvedUnit(Unit::Hour), resolved_smallest_unit.0); // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. let largest_unit = if unit.0 == Unit::Auto { @@ -329,10 +350,10 @@ impl ResolvedYearMonthLargestUnit { } else { ResolvedUnit::try_from_unit(unit.0)? }; - + // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. if ResolvedUnit::larger(largest_unit, resolved_smallest_unit.0) != largest_unit { - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(largest_unit)) } @@ -348,7 +369,7 @@ impl YearMonthUnitOrAuto { pub fn try_from_unit(unit: Unit) -> TemporalResult { if ![Unit::Year, Unit::Month].contains(&unit) && unit != Unit::Auto { // TODO: RangeError message. - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(unit)) } @@ -371,14 +392,13 @@ pub struct ResolvedUnit(Unit); impl ResolvedUnit { fn try_from_unit(unit: Unit) -> TemporalResult { if unit == Unit::Auto { - return Err(TemporalError::range()) + return Err(TemporalError::range()); } Ok(Self(unit)) } fn larger(u1: ResolvedUnit, u2: ResolvedUnit) -> ResolvedUnit { Self(Unit::larger(u1.0, u2.0).expect("no auto")) - } } @@ -397,12 +417,15 @@ impl fmt::Display for ResolvedUnit { } } - #[cfg(test)] mod tests { use core::str::FromStr; - use crate::options::{DateUnitOrAuto, NegatedRoundingMode, ResolvedPlainDateLargestUnit, ResolvedPlainDateSinceDifferenceSettings, ResolvedPlainDateSmallestUnit, ResolvedPlainDateUntilDifferenceSettings, RoundingIncrement, RoundingMode, Unit}; + use crate::options::{ + DateUnitOrAuto, NegatedRoundingMode, ResolvedPlainDateLargestUnit, + ResolvedPlainDateSinceDifferenceSettings, ResolvedPlainDateSmallestUnit, + ResolvedPlainDateUntilDifferenceSettings, RoundingIncrement, RoundingMode, Unit, + }; #[test] fn impl_plain_date_get_difference_settings() { @@ -439,7 +462,8 @@ mod tests { // 9. Let defaultLargestUnit be LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). // 10. If largestUnit is auto, set largestUnit to defaultLargestUnit. // 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. - let largest_unit = ResolvedPlainDateLargestUnit::try_from_units(largest_unit, smallest_unit).unwrap(); + let largest_unit = + ResolvedPlainDateLargestUnit::try_from_units(largest_unit, smallest_unit).unwrap(); // 12. Let maximum be MaximumTemporalDurationRoundingIncrement(smallestUnit). let maximum = smallest_unit.to_maximum_rounding_increment(); // 13. If maximum is not unset, perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false). @@ -461,4 +485,4 @@ mod tests { increment, }; } -} \ No newline at end of file +}