2022-06-30 00:58:40 +03:00
/*
* Copyright ( c ) 2022 , Idan Horowitz < idan . horowitz @ serenityos . org >
2022-08-30 11:04:10 -04:00
* Copyright ( c ) 2022 , Tim Flynn < trflynn89 @ serenityos . org >
2022-06-30 00:58:40 +03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-06-30 15:45:16 +03:00
# include <LibJS/Runtime/AbstractOperations.h>
2022-06-30 00:58:40 +03:00
# include <LibJS/Runtime/GlobalObject.h>
# include <LibJS/Runtime/Intl/DurationFormat.h>
2022-06-30 15:45:16 +03:00
# include <LibJS/Runtime/Intl/ListFormat.h>
# include <LibJS/Runtime/Intl/ListFormatConstructor.h>
# include <LibJS/Runtime/Intl/NumberFormatConstructor.h>
# include <LibJS/Runtime/Intl/PluralRules.h>
# include <LibJS/Runtime/Intl/PluralRulesConstructor.h>
# include <LibJS/Runtime/Intl/RelativeTimeFormat.h>
2022-06-30 09:43:20 +03:00
# include <LibJS/Runtime/Temporal/AbstractOperations.h>
2022-06-30 00:58:40 +03:00
namespace JS : : Intl {
// 1 DurationFormat Objects, https://tc39.es/proposal-intl-duration-format/#durationformat-objects
DurationFormat : : DurationFormat ( Object & prototype )
: Object ( prototype )
{
}
DurationFormat : : Style DurationFormat : : style_from_string ( StringView style )
{
if ( style = = " long " sv )
return Style : : Long ;
if ( style = = " short " sv )
return Style : : Short ;
if ( style = = " narrow " sv )
return Style : : Narrow ;
if ( style = = " digital " sv )
return Style : : Digital ;
VERIFY_NOT_REACHED ( ) ;
}
StringView DurationFormat : : style_to_string ( Style style )
{
switch ( style ) {
case Style : : Long :
return " long " sv ;
case Style : : Short :
return " short " sv ;
case Style : : Narrow :
return " narrow " sv ;
case Style : : Digital :
return " digital " sv ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
DurationFormat : : ValueStyle DurationFormat : : date_style_from_string ( StringView date_style )
{
if ( date_style = = " long " sv )
return ValueStyle : : Long ;
if ( date_style = = " short " sv )
return ValueStyle : : Short ;
if ( date_style = = " narrow " sv )
return ValueStyle : : Narrow ;
VERIFY_NOT_REACHED ( ) ;
}
DurationFormat : : ValueStyle DurationFormat : : time_style_from_string ( StringView time_style )
{
if ( time_style = = " long " sv )
return ValueStyle : : Long ;
if ( time_style = = " short " sv )
return ValueStyle : : Short ;
if ( time_style = = " narrow " sv )
return ValueStyle : : Narrow ;
if ( time_style = = " numeric " sv )
return ValueStyle : : Numeric ;
if ( time_style = = " 2-digit " sv )
return ValueStyle : : TwoDigit ;
VERIFY_NOT_REACHED ( ) ;
}
DurationFormat : : ValueStyle DurationFormat : : sub_second_style_from_string ( StringView sub_second_style )
{
if ( sub_second_style = = " long " sv )
return ValueStyle : : Long ;
if ( sub_second_style = = " short " sv )
return ValueStyle : : Short ;
if ( sub_second_style = = " narrow " sv )
return ValueStyle : : Narrow ;
if ( sub_second_style = = " numeric " sv )
return ValueStyle : : Numeric ;
VERIFY_NOT_REACHED ( ) ;
}
DurationFormat : : Display DurationFormat : : display_from_string ( StringView display )
{
if ( display = = " auto " sv )
return Display : : Auto ;
if ( display = = " always " sv )
return Display : : Always ;
VERIFY_NOT_REACHED ( ) ;
}
StringView DurationFormat : : value_style_to_string ( ValueStyle value_style )
{
switch ( value_style ) {
case ValueStyle : : Long :
return " long " sv ;
case ValueStyle : : Short :
return " short " sv ;
case ValueStyle : : Narrow :
return " narrow " sv ;
case ValueStyle : : Numeric :
return " numeric " sv ;
case ValueStyle : : TwoDigit :
return " 2-digit " sv ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
StringView DurationFormat : : display_to_string ( Display display )
{
switch ( display ) {
case Display : : Auto :
return " auto " sv ;
case Display : : Always :
return " always " sv ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
2022-07-02 15:55:14 +03:00
// 1.1.3 ToDurationRecord ( input ), https://tc39.es/proposal-intl-duration-format/#sec-todurationrecord
2022-08-20 08:25:24 +01:00
ThrowCompletionOr < Temporal : : DurationRecord > to_duration_record ( VM & vm , Value input )
2022-06-30 09:43:20 +03:00
{
// 1. If Type(input) is not Object, throw a TypeError exception.
if ( ! input . is_object ( ) )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < TypeError > ( ErrorType : : NotAnObject , input ) ;
2022-06-30 09:43:20 +03:00
auto & input_object = input . as_object ( ) ;
2022-08-30 11:19:43 -04:00
// 2. Let result be a new Duration Record with each field set to 0.
Temporal : : DurationRecord result = { } ;
2022-06-30 09:43:20 +03:00
// 3. Let any be false.
auto any = false ;
2022-08-30 11:24:17 -04:00
// 4. For each row of Table 1, except the header row, in table order, do
2022-06-30 09:43:20 +03:00
for ( auto const & duration_instances_component : duration_instances_components ) {
2022-08-30 11:19:43 -04:00
// a. Let valueSlot be the Value Slot value of the current row.
2022-06-30 09:43:20 +03:00
auto value_slot = duration_instances_component . value_slot ;
2022-08-30 11:19:43 -04:00
// b. Let unit be the Unit value of the current row.
2022-06-30 09:43:20 +03:00
auto unit = duration_instances_component . unit ;
// c. Let value be ? Get(input, unit).
auto value = TRY ( input_object . get ( FlyString ( unit ) ) ) ;
// d. If value is not undefined, then
if ( ! value . is_undefined ( ) ) {
// i. Set any to true.
any = true ;
2022-08-30 11:19:43 -04:00
2022-06-30 09:43:20 +03:00
// ii. Set value to ? ToIntegerWithoutRounding(value).
2022-08-30 11:19:43 -04:00
auto value_number = TRY ( Temporal : : to_integer_without_rounding ( vm , value , ErrorType : : TemporalInvalidDurationPropertyValueNonIntegral , unit , value ) ) ;
2022-06-30 09:43:20 +03:00
2022-08-30 11:19:43 -04:00
// iii. Set result.[[<valueSlot>]] to value.
result . * value_slot = value_number ;
}
2022-06-30 09:43:20 +03:00
}
// 5. If any is false, throw a TypeError exception.
if ( ! any )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < TypeError > ( ErrorType : : TemporalInvalidDurationLikeObject ) ;
2022-06-30 09:43:20 +03:00
// 6. Return result.
return result ;
}
2022-08-30 11:21:38 -04:00
// 1.1.4 DurationRecordSign ( record ), https://tc39.es/proposal-intl-duration-format/#sec-durationrecordsign
i8 duration_record_sign ( Temporal : : DurationRecord const & record )
2022-07-02 15:55:14 +03:00
{
2022-08-30 11:24:17 -04:00
// 1. For each row of Table 1, except the header row, in table order, do
2022-07-02 15:55:14 +03:00
for ( auto const & duration_instances_component : duration_instances_components ) {
2022-08-30 11:24:17 -04:00
// a. Let valueSlot be the Value Slot value of the current row.
2022-07-02 15:55:14 +03:00
auto value_slot = duration_instances_component . value_slot ;
2022-08-30 11:24:17 -04:00
// b. Let v be record.[[<valueSlot>]].
2022-07-02 15:55:14 +03:00
auto value = record . * value_slot ;
// c. If v < 0, return -1.
if ( value < 0 )
return - 1 ;
// d. If v > 0, return 1.
if ( value > 0 )
return 1 ;
}
// 2. Return 0.
return 0 ;
}
// 1.1.5 IsValidDurationRecord ( record ), https://tc39.es/proposal-intl-duration-format/#sec-isvaliddurationrecord
bool is_valid_duration_record ( Temporal : : DurationRecord const & record )
{
2022-08-30 11:21:38 -04:00
// 1. Let sign be ! DurationRecordSign(record).
auto sign = duration_record_sign ( record ) ;
2022-07-02 15:55:14 +03:00
2022-08-30 11:24:17 -04:00
// 2. For each row of Table 1, except the header row, in table order, do
2022-07-02 15:55:14 +03:00
for ( auto const & duration_instances_component : duration_instances_components ) {
2022-08-30 11:24:17 -04:00
// a. Let valueSlot be the Value Slot value of the current row.
2022-07-02 15:55:14 +03:00
auto value_slot = duration_instances_component . value_slot ;
2022-08-30 11:24:17 -04:00
// b. Let v be record.[[<valueSlot>]].
2022-07-02 15:55:14 +03:00
auto value = record . * value_slot ;
2022-08-30 11:24:17 -04:00
// c. Assert: 𝔽 (v) is finite.
VERIFY ( isfinite ( value ) ) ;
2022-07-02 15:55:14 +03:00
// d. If v < 0 and sign > 0, return false.
if ( value < 0 & & sign > 0 )
return false ;
// e. If v > 0 and sign < 0, return false.
if ( value > 0 & & sign < 0 )
return false ;
}
// 3. Return true.
return true ;
}
// 1.1.6 GetDurationUnitOptions ( unit, options, baseStyle, stylesList, digitalBase, prevStyle ), https://tc39.es/proposal-intl-duration-format/#sec-getdurationunitoptions
2022-08-30 11:13:33 -04:00
ThrowCompletionOr < DurationUnitOptions > get_duration_unit_options ( VM & vm , String const & unit , Object const & options , StringView base_style , Span < StringView const > styles_list , StringView digital_base , StringView previous_style )
2022-06-30 00:58:40 +03:00
{
// 1. Let style be ? GetOption(options, unit, "string", stylesList, undefined).
2022-08-20 08:52:42 +01:00
auto style_value = TRY ( get_option ( vm , options , unit , OptionType : : String , styles_list , Empty { } ) ) ;
2022-06-30 00:58:40 +03:00
// 2. Let displayDefault be "always".
auto display_default = " always " sv ;
String style ;
// 3. If style is undefined, then
if ( style_value . is_undefined ( ) ) {
// a. Set displayDefault to "auto".
display_default = " auto " sv ;
// b. If baseStyle is "digital", then
if ( base_style = = " digital " sv ) {
// i. Set style to digitalBase.
style = digital_base ;
}
2022-08-30 11:42:02 -04:00
// c. Else if prevStyle is "numeric" or "2-digit", then
else if ( previous_style = = " numeric " sv | | previous_style = = " 2-digit " sv ) {
// i. Set style to "numeric".
style = " numeric " sv ;
}
// d. Else,
2022-06-30 00:58:40 +03:00
else {
// i. Set style to baseStyle.
style = base_style ;
}
} else {
style = style_value . as_string ( ) . string ( ) ;
}
// 4. Let displayField be the string-concatenation of unit and "Display".
auto display_field = String : : formatted ( " {}Display " , unit ) ;
// 5. Let display be ? GetOption(options, displayField, "string", « "auto", "always" », displayDefault).
2022-08-20 08:52:42 +01:00
auto display = TRY ( get_option ( vm , options , display_field , OptionType : : String , { " auto " sv , " always " sv } , display_default ) ) ;
2022-06-30 00:58:40 +03:00
// 6. If prevStyle is "numeric" or "2-digit", then
if ( previous_style = = " numeric " sv | | previous_style = = " 2-digit " sv ) {
// a. If style is not "numeric" or "2-digit", then
if ( style ! = " numeric " sv & & style ! = " 2-digit " sv ) {
// i. Throw a RangeError exception.
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : IntlNonNumericOr2DigitAfterNumericOr2Digit ) ;
2022-06-30 00:58:40 +03:00
}
// b. Else if unit is "minutes" or "seconds", then
else if ( unit = = " minutes " sv | | unit = = " seconds " sv ) {
// i. Set style to "2-digit".
style = " 2-digit " sv ;
}
}
// 7. Return the Record { [[Style]]: style, [[Display]]: display }.
return DurationUnitOptions { . style = move ( style ) , . display = display . as_string ( ) . string ( ) } ;
}
2022-06-30 15:45:16 +03:00
// FIXME: LibUnicode currently only exposes unit patterns converted to an ECMA402 NumberFormat-specific format,
// since DurationFormat only needs a tiny subset of it, it's much easier to just convert it to the expected format
// here, but at some point we should split the the NumberFormat exporter to export both formats of the data.
2022-09-02 12:01:10 -04:00
static String convert_number_format_pattern_to_duration_format_template ( : : Locale : : NumberFormat const & number_format )
2022-06-30 15:45:16 +03:00
{
2022-07-11 17:32:29 +00:00
auto result = number_format . zero_format . replace ( " {number} " sv , " {0} " sv , ReplaceMode : : FirstOnly ) ;
2022-06-30 15:45:16 +03:00
for ( size_t i = 0 ; i < number_format . identifiers . size ( ) ; + + i )
2022-07-05 22:33:15 +02:00
result = result . replace ( String : : formatted ( " {{unitIdentifier:{}}} " , i ) , number_format . identifiers [ i ] , ReplaceMode : : FirstOnly ) ;
2022-06-30 15:45:16 +03:00
return result ;
}
2022-07-02 15:55:14 +03:00
// 1.1.7 PartitionDurationFormatPattern ( durationFormat, duration ), https://tc39.es/proposal-intl-duration-format/#sec-partitiondurationformatpattern
2022-08-30 11:16:38 -04:00
Vector < PatternPartition > partition_duration_format_pattern ( VM & vm , DurationFormat const & duration_format , Temporal : : DurationRecord const & duration )
2022-06-30 15:45:16 +03:00
{
2022-08-20 08:25:24 +01:00
auto & realm = * vm . current_realm ( ) ;
2022-06-30 15:45:16 +03:00
// 1. Let result be a new empty List.
Vector < PatternPartition > result ;
2022-08-30 11:04:10 -04:00
// 2. Let done be false.
bool done = false ;
// 3. While done is false, repeat for each row in Table 1 in order, except the header row:
for ( size_t i = 0 ; ! done & & i < duration_instances_components . size ( ) ; + + i ) {
2022-06-30 15:45:16 +03:00
auto const & duration_instances_component = duration_instances_components [ i ] ;
// a. Let styleSlot be the Style Slot value.
auto style_slot = duration_instances_component . get_style_slot ;
2022-08-30 11:04:10 -04:00
// b. Let displaySlot be the Display Slot value.
2022-06-30 15:45:16 +03:00
auto display_slot = duration_instances_component . get_display_slot ;
2022-08-30 11:04:10 -04:00
// c. Let valueSlot be the Value Slot value.
2022-06-30 15:45:16 +03:00
auto value_slot = duration_instances_component . value_slot ;
2022-08-30 11:04:10 -04:00
// d. Let unit be the Unit value.
2022-06-30 15:45:16 +03:00
auto unit = duration_instances_component . unit ;
2022-08-30 11:04:10 -04:00
// e. Let style be durationFormat.[[<styleSlot>]].
2022-06-30 15:45:16 +03:00
auto style = ( duration_format . * style_slot ) ( ) ;
2022-08-30 11:04:10 -04:00
// f. Let display be durationFormat.[[<displaySlot>]].
auto display = ( duration_format . * display_slot ) ( ) ;
// g. Let value be duration.[[<valueSlot>]].
auto value = duration . * value_slot ;
2022-06-30 15:45:16 +03:00
// h. Let nfOpts be ! OrdinaryObjectCreate(null).
2022-08-16 00:20:49 +01:00
auto * number_format_options = Object : : create ( realm , nullptr ) ;
2022-06-30 15:45:16 +03:00
2022-08-30 11:04:10 -04:00
// i. If unit is "seconds", "milliseconds", or "microseconds", then
if ( unit . is_one_of ( " seconds " sv , " milliseconds " sv , " microseconds " sv ) ) {
DurationFormat : : ValueStyle next_style ;
2022-06-30 15:45:16 +03:00
2022-08-30 11:04:10 -04:00
// i. If unit is "seconds", then
if ( unit = = " seconds " sv ) {
// 1. Let nextStyle be durationFormat.[[MillisecondsStyle]].
next_style = duration_format . milliseconds_style ( ) ;
}
// ii. Else if unit is "milliseconds", then
else if ( unit = = " milliseconds " sv ) {
// 1. Let nextStyle be durationFormat.[[MicrosecondsStyle]].
next_style = duration_format . microseconds_style ( ) ;
}
// iii. Else,
else {
// 1. Let nextStyle be durationFormat.[[NanosecondsStyle]].
next_style = duration_format . nanoseconds_style ( ) ;
}
2022-06-30 15:45:16 +03:00
2022-08-30 11:04:10 -04:00
// iv. If nextStyle is "numeric", then
if ( next_style = = DurationFormat : : ValueStyle : : Numeric ) {
// 1. If unit is "seconds", then
2022-06-30 15:45:16 +03:00
if ( unit = = " seconds " sv ) {
2022-08-30 11:04:10 -04:00
// a. Set value to value + duration.[[Milliseconds]] / 10^3 + duration.[[Microseconds]] / 10^6 + duration.[[Nanoseconds]] / 10^9.
value + = duration . milliseconds / 1'000 .0 + duration . microseconds / 1'000'000 .0 + duration . nanoseconds / 1'000'000'000 .0 ;
}
// 2. Else if unit is "milliseconds", then
else if ( unit = = " milliseconds " sv ) {
// a. Set value to value + duration.[[Microseconds]] / 10^3 + duration.[[Nanoseconds]] / 10^6.
value + = duration . microseconds / 1'000 .0 + duration . nanoseconds / 1'000'000 .0 ;
}
// 3. Else,
else {
// a. Set value to value + duration.[[Nanoseconds]] / 10^3.
value + = duration . nanoseconds / 1'000 .0 ;
2022-06-30 15:45:16 +03:00
}
2022-08-30 11:04:10 -04:00
// 4. Perform ! CreateDataPropertyOrThrow(nfOpts, "maximumFractionDigits", durationFormat.[[FractionalDigits]]).
MUST ( number_format_options - > create_data_property_or_throw ( vm . names . maximumFractionDigits , duration_format . has_fractional_digits ( ) ? Value ( duration_format . fractional_digits ( ) ) : js_undefined ( ) ) ) ;
2022-06-30 15:45:16 +03:00
2022-08-30 11:04:10 -04:00
// 5. Perform ! CreateDataPropertyOrThrow(nfOpts, "minimumFractionDigits", durationFormat.[[FractionalDigits]]).
MUST ( number_format_options - > create_data_property_or_throw ( vm . names . minimumFractionDigits , duration_format . has_fractional_digits ( ) ? Value ( duration_format . fractional_digits ( ) ) : js_undefined ( ) ) ) ;
2022-07-02 15:58:34 +03:00
2022-08-30 11:04:10 -04:00
// 6. Set done to true.
done = true ;
}
2022-06-30 15:45:16 +03:00
}
2022-08-30 11:04:10 -04:00
// j. If style is "2-digit", then
2022-06-30 15:45:16 +03:00
if ( style = = DurationFormat : : ValueStyle : : TwoDigit ) {
2022-08-30 11:04:10 -04:00
// i. Perform ! CreateDataPropertyOrThrow(nfOpts, "minimumIntegerDigits", 2𝔽 ).
2022-06-30 15:45:16 +03:00
MUST ( number_format_options - > create_data_property_or_throw ( vm . names . minimumIntegerDigits , Value ( 2 ) ) ) ;
}
2022-08-30 11:04:10 -04:00
// k. If value is not 0 or display is not "auto", then
if ( value ! = 0.0 | | display ! = DurationFormat : : Display : : Auto ) {
// i. Let nf be ! Construct(%NumberFormat%, « durationFormat.[[Locale]], nfOpts »).
auto * number_format = static_cast < NumberFormat * > ( MUST ( construct ( vm , * realm . intrinsics ( ) . intl_number_format_constructor ( ) , js_string ( vm , duration_format . locale ( ) ) , number_format_options ) ) ) ;
// ii. Let dataLocale be durationFormat.[[DataLocale]].
auto const & data_locale = duration_format . data_locale ( ) ;
// iii. Let dataLocaleData be %DurationFormat%.[[LocaleData]].[[<dataLocale>]].
// iv. If style is "2-digit" or "numeric", then
if ( style = = DurationFormat : : ValueStyle : : TwoDigit | | style = = DurationFormat : : ValueStyle : : Numeric ) {
// 1. Let num be ! FormatNumeric(nf, 𝔽 (value)).
auto number = format_numeric ( vm , * number_format , MathematicalValue ( value ) ) ;
// 2. Append the new Record { [[Type]]: unit, [[Value]]: num} to the end of result.
result . append ( { unit , number } ) ;
// 3. If unit is "hours" or "minutes", then
if ( unit . is_one_of ( " hours " sv , " minutes " sv ) ) {
double next_value = 0.0 ;
DurationFormat : : Display next_display ;
// a. If unit is "hours", then
if ( unit = = " hours " sv ) {
// i. Let nextValue be duration.[[Minutes]].
next_value = duration . minutes ;
// ii. Let nextDisplay be durationFormat.[[MinutesDisplay]].
next_display = duration_format . minutes_display ( ) ;
}
// b. Else,
else {
// i. Let nextValue be duration.[[Seconds]].
next_value = duration . seconds ;
// ii. Let nextDisplay be durationFormat.[[SecondsDisplay]].
next_display = duration_format . seconds_display ( ) ;
// iii. If durationFormat.[[MillisecondsStyle]] is "numeric", then
if ( duration_format . milliseconds_style ( ) = = DurationFormat : : ValueStyle : : Numeric ) {
// i. Set nextValue to nextValue + duration.[[Milliseconds]] / 10^3 + duration.[[Microseconds]] / 10^6 + duration.[[Nanoseconds]] / 10^9.
next_value + = duration . milliseconds / 1'000 .0 + duration . microseconds / 1'000'000 .0 + duration . nanoseconds / 1'000'000'000 .0 ;
}
}
// c. If nextValue is not 0 or nextDisplay is not "auto", then
if ( next_value ! = 0.0 | | next_display ! = DurationFormat : : Display : : Auto ) {
// i. Let separator be dataLocaleData.[[formats]].[[digital]].[[separator]].
2022-09-02 12:01:10 -04:00
auto separator = : : Locale : : get_number_system_symbol ( data_locale , duration_format . numbering_system ( ) , : : Locale : : NumericSymbol : : TimeSeparator ) . value_or ( " : " sv ) ;
2022-08-30 11:04:10 -04:00
// ii. Append the new Record { [[Type]]: "literal", [[Value]]: separator} to the end of result.
result . append ( { " literal " sv , separator } ) ;
}
}
2022-06-30 15:45:16 +03:00
}
2022-08-30 11:04:10 -04:00
// v. Else,
else {
// 1. Let num be ! PartitionNumberPattern(nf, 𝔽 (value)).
auto number = partition_number_pattern ( vm , * number_format , MathematicalValue ( value ) ) ;
2022-06-30 15:45:16 +03:00
2022-08-30 11:04:10 -04:00
// 2. Let pr be ! Construct(%PluralRules%, « durationFormat.[[Locale]] »).
auto * plural_rules = static_cast < PluralRules * > ( MUST ( construct ( vm , * realm . intrinsics ( ) . intl_plural_rules_constructor ( ) , js_string ( vm , duration_format . locale ( ) ) ) ) ) ;
2022-07-08 10:31:05 -04:00
2022-08-30 11:04:10 -04:00
// 3. Let prv be ! ResolvePlural(pr, 𝔽 (value)).
auto plurality = resolve_plural ( * plural_rules , Value ( value ) ) ;
2022-07-08 10:31:05 -04:00
2022-09-02 12:01:10 -04:00
auto formats = : : Locale : : get_unit_formats ( data_locale , duration_instances_component . unit_singular , static_cast < : : Locale : : Style > ( style ) ) ;
2022-08-30 11:04:10 -04:00
auto pattern = formats . find_if ( [ & ] ( auto & p ) { return p . plurality = = plurality ; } ) ;
if ( pattern = = formats . end ( ) )
continue ;
2022-06-30 15:45:16 +03:00
2022-08-30 11:04:10 -04:00
// 4. Let template be dataLocaleData.[[formats]].[[<style>]].[[<unit>]].[[<prv>]].
auto template_ = convert_number_format_pattern_to_duration_format_template ( * pattern ) ;
2022-06-30 15:45:16 +03:00
2022-08-30 11:04:10 -04:00
// 5. Let parts be ! MakePartsList(template, unit, num).
auto parts = make_parts_list ( template_ , unit , move ( number ) ) ;
2022-06-30 15:45:16 +03:00
2022-08-30 11:04:10 -04:00
// 6. Let concat be an empty String.
StringBuilder concat ;
2022-06-30 15:45:16 +03:00
2022-08-30 11:04:10 -04:00
// 7. For each Record { [[Type]], [[Value]], [[Unit]] } part in parts, do
for ( auto const & part : parts ) {
// a. Set concat to the string-concatenation of concat and part.[[Value]].
concat . append ( part . value ) ;
}
2022-06-30 15:45:16 +03:00
2022-08-30 11:04:10 -04:00
// 8. Append the new Record { [[Type]]: unit, [[Value]]: concat } to the end of result.
result . append ( { unit , concat . build ( ) } ) ;
2022-06-30 15:45:16 +03:00
}
}
}
2022-08-30 12:04:11 -04:00
// 4. Let lfOpts be ! OrdinaryObjectCreate(null).
auto * list_format_options = Object : : create ( realm , nullptr ) ;
// 5. Perform ! CreateDataPropertyOrThrow(lfOpts, "type", "unit").
MUST ( list_format_options - > create_data_property_or_throw ( vm . names . type , js_string ( vm , " unit " sv ) ) ) ;
// 6. Let listStyle be durationFormat.[[Style]].
auto list_style = duration_format . style ( ) ;
// 7. If listStyle is "digital", then
if ( list_style = = DurationFormat : : Style : : Digital ) {
// a. Set listStyle to "narrow".
list_style = DurationFormat : : Style : : Narrow ;
}
2022-09-02 12:01:10 -04:00
auto unicode_list_style = : : Locale : : style_to_string ( static_cast < : : Locale : : Style > ( list_style ) ) ;
2022-08-30 12:04:11 -04:00
// 8. Perform ! CreateDataPropertyOrThrow(lfOpts, "style", listStyle).
MUST ( list_format_options - > create_data_property_or_throw ( vm . names . style , js_string ( vm , unicode_list_style ) ) ) ;
// 9. Let lf be ! Construct(%ListFormat%, « durationFormat.[[Locale]], lfOpts »).
auto * list_format = static_cast < ListFormat * > ( MUST ( construct ( vm , * realm . intrinsics ( ) . intl_list_format_constructor ( ) , js_string ( vm , duration_format . locale ( ) ) , list_format_options ) ) ) ;
2022-06-30 15:45:16 +03:00
// FIXME: CreatePartsFromList expects a list of strings and creates a list of Pattern Partition records, but we already created a list of Pattern Partition records
// so we try to hack something together from it that looks mostly right
Vector < String > string_result ;
bool merge = false ;
for ( size_t i = 0 ; i < result . size ( ) ; + + i ) {
auto const & part = result [ i ] ;
if ( part . type = = " literal " ) {
string_result . last ( ) = String : : formatted ( " {}{} " , string_result . last ( ) , part . value ) ;
merge = true ;
continue ;
}
if ( merge ) {
string_result . last ( ) = String : : formatted ( " {}{} " , string_result . last ( ) , part . value ) ;
merge = false ;
continue ;
}
string_result . append ( part . value ) ;
}
2022-08-30 12:04:11 -04:00
// 10. Set result to ! CreatePartsFromList(lf, result).
2022-06-30 15:45:16 +03:00
auto final_result = create_parts_from_list ( * list_format , string_result ) ;
2022-08-30 12:04:11 -04:00
// 11. Return result.
2022-06-30 15:45:16 +03:00
return final_result ;
}
2022-06-30 00:58:40 +03:00
}