22// License, v. 2.0. If a copy of the MPL was not distributed with this
33// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44
5- use std:: num:: NonZeroU32 ;
5+ use std:: { num:: NonZeroU32 , str :: FromStr } ;
66
7- use temporal_rs:: options:: { RoundingIncrement , RoundingMode } ;
7+ use temporal_rs:: options:: { RoundingIncrement , RoundingMode , Unit } ;
88
99use crate :: {
1010 ecmascript:: {
11- abstract_operations:: {
12- operations_on_objects:: get,
13- type_conversion:: { to_boolean, to_string} ,
14- } ,
11+ abstract_operations:: { operations_on_objects:: get, type_conversion:: to_string} ,
1512 builtins:: {
1613 ordinary:: ordinary_object_create_with_intrinsics,
1714 temporal:: instant:: instant_prototype:: to_integer_with_truncation,
1815 } ,
1916 execution:: { Agent , JsResult , agent:: ExceptionType } ,
20- types:: { BUILTIN_STRING_MEMORY , Object , PropertyKey , String , Value } ,
17+ types:: { BUILTIN_STRING_MEMORY , Object , PropertyKey , Value } ,
2118 } ,
22- engine:: context:: { Bindable , GcScope , NoGcScope , trivially_bindable } ,
19+ engine:: context:: { Bindable , GcScope , NoGcScope } ,
2320} ;
2421
25- pub ( crate ) enum OptionType {
26- Boolean ( bool ) ,
27- String ( String < ' static > ) ,
22+ pub trait OptionType : Sized {
23+ fn from_string ( s : & str ) -> Result < Self , std:: string:: String > ;
24+ }
25+
26+ impl OptionType for RoundingMode {
27+ fn from_string ( s : & str ) -> Result < Self , std:: string:: String > {
28+ RoundingMode :: from_str ( s) . map_err ( |e| e. to_string ( ) )
29+ }
2830}
2931
30- trivially_bindable ! ( OptionType ) ;
31- trivially_bindable ! ( RoundingMode ) ;
32- trivially_bindable ! ( RoundingIncrement ) ;
32+ impl OptionType for Unit {
33+ fn from_string ( s : & str ) -> Result < Self , std:: string:: String > {
34+ Unit :: from_str ( s) . map_err ( |e| e. to_string ( ) )
35+ }
36+ }
3337
3438/// ### [14.5.2.1 GetOptionsObject ( options )](https://tc39.es/proposal-temporal/#sec-getoptionsobject)
3539///
@@ -52,9 +56,11 @@ pub(crate) fn get_options_object<'gc>(
5256 ) )
5357 }
5458 // 2. If options is an Object, then
55- Value :: Object ( obj ) => {
59+ value if value . is_object ( ) => {
5660 // a. Return options.
57- Ok ( obj. into ( ) )
61+ // TODO: remove unwrap; Although safe because value is an object
62+ let obj = Object :: try_from ( value) . unwrap ( ) ;
63+ Ok ( obj)
5864 }
5965 // 3. Throw a TypeError exception.
6066 _ => Err ( agent. throw_exception_with_static_message (
@@ -74,13 +80,15 @@ pub(crate) fn get_options_object<'gc>(
7480/// specified property of options, converts it to the required type, checks whether it is allowed
7581/// by values if values is not empty, and substitutes default if the value is undefined. It
7682/// performs the following steps when called:
77- pub ( crate ) fn get_option < ' gc > (
83+ pub ( crate ) fn get_option < ' gc , T > (
7884 agent : & mut Agent ,
7985 options : Object ,
8086 property : PropertyKey ,
81- typee : OptionType ,
8287 mut gc : GcScope < ' gc , ' _ > ,
83- ) -> JsResult < ' gc , OptionType > {
88+ ) -> JsResult < ' gc , Option < T > >
89+ where
90+ T : OptionType ,
91+ {
8492 let options = options. bind ( gc. nogc ( ) ) ;
8593 let property = property. bind ( gc. nogc ( ) ) ;
8694 // 1. Let value be ? Get(options, property).
@@ -91,27 +99,40 @@ pub(crate) fn get_option<'gc>(
9199 if value. is_undefined ( ) {
92100 // a. If default is required, throw a RangeError exception.
93101 // b. Return default.
94- todo ! ( )
95- }
96- match typee {
97- // 3. If type is boolean, then
98- OptionType :: Boolean ( _) => {
99- // a. Set value to ToBoolean(value).
100- let value = to_boolean ( agent, value) ;
101- }
102- // 4. Else,
103- OptionType :: String ( _) => {
104- // a. Assert: type is string.
105- // b. Set value to ? ToString(value).
106- let value = to_string ( agent, value. unbind ( ) , gc. reborrow ( ) )
107- . unbind ( ) ?
108- . bind ( gc. nogc ( ) ) ;
109- }
102+ return Ok ( None ) ;
110103 }
111104
105+ // 3. If type is boolean, then
106+ // a. Set value to ToBoolean(value).
107+ // 4. Else,
108+ // a. Assert: type is string.
109+ // b. Set value to ? ToString(value).
112110 // 5. If values is not empty and values does not contain value, throw a RangeError exception.
111+
112+ // TODO: Currently only works for temporal_rs::Unit, and temporal_rs::RoundingMode.
113+ //
114+ // Should be extended to work with
115+ // 1. ecmascript::types::String
116+ // 2. bool
117+ // 3. Potentially other temporal_rs types.
118+
119+ let js_str = to_string ( agent, value. unbind ( ) , gc. reborrow ( ) )
120+ . unbind ( ) ?
121+ . bind ( gc. nogc ( ) ) ;
122+
123+ // TODO: Fix this code.. None case is unreachable but code sucks rn..
124+ let rust_str = js_str. as_str ( agent) . unwrap ( ) ;
125+
126+ let parsed = T :: from_string ( rust_str) . map_err ( |msg| {
127+ agent. throw_exception_with_static_message (
128+ ExceptionType :: RangeError ,
129+ Box :: leak ( msg. into_boxed_str ( ) ) ,
130+ gc. into_nogc ( ) ,
131+ )
132+ } ) ?;
133+
113134 // 6. Return value.
114- todo ! ( )
135+ Ok ( Some ( parsed ) )
115136}
116137
117138/// ### [14.5.2.3 GetRoundingModeOption ( options, fallback)](https://tc39.es/proposal-temporal/#sec-temporal-getroundingmodeoption)
@@ -121,18 +142,27 @@ pub(crate) fn get_option<'gc>(
121142// completion. It fetches and validates the "roundingMode" property from options, returning
122143// fallback as a default if absent. It performs the following steps when called:
123144pub ( crate ) fn get_rounding_mode_option < ' gc > (
124- _agent : & mut Agent ,
145+ agent : & mut Agent ,
125146 options : Object ,
126147 fallback : RoundingMode ,
127148 gc : GcScope < ' gc , ' _ > ,
128149) -> JsResult < ' gc , RoundingMode > {
129- let _options = options. bind ( gc. nogc ( ) ) ;
130- let _fallback = fallback. bind ( gc. nogc ( ) ) ;
150+ let options = options. bind ( gc. nogc ( ) ) ;
151+ let fallback = fallback. bind ( gc. nogc ( ) ) ;
152+
131153 // 1. Let allowedStrings be the List of Strings from the "String Identifier" column of Table 28.
132154 // 2. Let stringFallback be the value from the "String Identifier" column of the row with fallback in its "Rounding Mode" column.
133155 // 3. Let stringValue be ? GetOption(options, "roundingMode", string, allowedStrings, stringFallback).
134156 // 4. Return the value from the "Rounding Mode" column of the row with stringValue in its "String Identifier" column.
135- todo ! ( )
157+ match get_option :: < RoundingMode > (
158+ agent,
159+ options. unbind ( ) ,
160+ BUILTIN_STRING_MEMORY . roundingMode . into ( ) ,
161+ gc,
162+ ) ? {
163+ Some ( mode) => Ok ( mode) ,
164+ None => Ok ( fallback) ,
165+ }
136166}
137167
138168/// ### [14.5.2.4 GetRoundingIncrementOption ( options )](https://tc39.es/proposal-temporal/#sec-temporal-getroundingincrementoption)
@@ -165,15 +195,14 @@ pub(crate) fn get_rounding_increment_option<'gc>(
165195 . bind ( gc. nogc ( ) ) ;
166196
167197 // 4. If integerIncrement < 1 or integerIncrement > 10**9, throw a RangeError exception.
168- if integer_increment < 1.0 || integer_increment > 1_000_000_000.0 {
198+ if ! ( 1.0 ..= 1_000_000_000.0 ) . contains ( & integer_increment ) {
169199 return Err ( agent. throw_exception_with_static_message (
170200 ExceptionType :: RangeError ,
171201 "roundingIncrement must be between 1 and 10**9" ,
172202 gc. into_nogc ( ) ,
173203 ) ) ;
174204 }
175205
176- // Convert safely and return integerIncrement
177206 // NOTE: `as u32` is safe here since we validated it’s in range.
178207 let integer_increment_u32 = integer_increment as u32 ;
179208 let increment =
0 commit comments