1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 #ifndef intl_components_NumberRangeFormat_h_
5 #define intl_components_NumberRangeFormat_h_
7 #include "mozilla/FloatingPoint.h"
8 #include "mozilla/intl/ICUError.h"
9 #include "mozilla/intl/NumberFormat.h"
10 #include "mozilla/Result.h"
11 #include "mozilla/UniquePtr.h"
14 #include <string_view>
16 #include "unicode/utypes.h"
18 struct UFormattedNumberRange
;
19 struct UNumberRangeFormatter
;
22 namespace mozilla::intl
{
25 * NumberRangeFormatOptions supports the same set of options as
26 * NumberFormatOptions and additionally allows to control how to display ranges.
28 struct MOZ_STACK_CLASS NumberRangeFormatOptions
: public NumberFormatOptions
{
30 * Controls if and how to collapse identical parts in a range.
32 enum class RangeCollapse
{
34 * Apply locale-specific heuristics.
39 * Never collapse identical parts.
44 * Collapse identical unit parts.
49 * Collapse all identical parts.
52 } mRangeCollapse
= RangeCollapse::Auto
;
55 * Controls how to display identical numbers.
57 enum class RangeIdentityFallback
{
59 * Display the range as a single value.
64 * Display the range as a single value if both numbers were equal before
65 * rounding. Otherwise display with a locale-sensitive approximation
68 ApproximatelyOrSingleValue
,
71 * Display with a locale-sensitive approximation pattern.
76 * Display as a range expression.
79 } mRangeIdentityFallback
= RangeIdentityFallback::SingleValue
;
83 * A NumberRangeFormat implementation that roughly mirrors the API provided by
84 * the ECMA-402 Intl.NumberFormat object for formatting number ranges.
86 * https://tc39.es/ecma402/#numberformat-objects
88 class NumberRangeFormat final
{
91 * Initialize a new NumberRangeFormat for the provided locale and using the
94 * https://tc39.es/ecma402/#sec-initializenumberformat
96 static Result
<UniquePtr
<NumberRangeFormat
>, ICUError
> TryCreate(
97 std::string_view aLocale
, const NumberRangeFormatOptions
& aOptions
);
99 NumberRangeFormat() = default;
100 NumberRangeFormat(const NumberRangeFormat
&) = delete;
101 NumberRangeFormat
& operator=(const NumberRangeFormat
&) = delete;
103 ~NumberRangeFormat();
106 * Formats a double range to a utf-16 string. The string view is valid until
107 * another number range is formatted. Accessing the string view after this
108 * event is undefined behavior.
110 * https://tc39.es/ecma402/#sec-formatnumericrange
112 Result
<std::u16string_view
, ICUError
> format(double start
, double end
) const {
113 if (!formatInternal(start
, end
)) {
114 return Err(ICUError::InternalError
);
117 return formatResult();
121 * Formats a double range to a utf-16 string, and fills the provided parts
122 * vector. The string view is valid until another number is formatted.
123 * Accessing the string view after this event is undefined behavior.
125 * https://tc39.es/ecma402/#sec-partitionnumberrangepattern
127 Result
<std::u16string_view
, ICUError
> formatToParts(
128 double start
, double end
, NumberPartVector
& parts
) const {
129 if (!formatInternal(start
, end
)) {
130 return Err(ICUError::InternalError
);
133 bool isNegativeStart
= !IsNaN(start
) && IsNegative(start
);
134 bool isNegativeEnd
= !IsNaN(end
) && IsNegative(end
);
136 return formatResultToParts(Some(start
), isNegativeStart
, Some(end
),
137 isNegativeEnd
, parts
);
141 * Formats a decimal number range to a utf-16 string. The string view is valid
142 * until another number range is formatted. Accessing the string view after
143 * this event is undefined behavior.
145 * https://tc39.es/ecma402/#sec-formatnumericrange
147 Result
<std::u16string_view
, ICUError
> format(std::string_view start
,
148 std::string_view end
) const {
149 if (!formatInternal(start
, end
)) {
150 return Err(ICUError::InternalError
);
153 return formatResult();
157 * Formats a string encoded decimal number range to a utf-16 string, and fills
158 * the provided parts vector. The string view is valid until another number is
159 * formatted. Accessing the string view after this event is undefined
162 * https://tc39.es/ecma402/#sec-partitionnumberrangepattern
164 Result
<std::u16string_view
, ICUError
> formatToParts(
165 std::string_view start
, std::string_view end
,
166 NumberPartVector
& parts
) const {
167 if (!formatInternal(start
, end
)) {
168 return Err(ICUError::InternalError
);
171 Maybe
<double> numStart
= Nothing();
172 if (start
== "Infinity" || start
== "+Infinity") {
173 numStart
.emplace(PositiveInfinity
<double>());
174 } else if (start
== "-Infinity") {
175 numStart
.emplace(NegativeInfinity
<double>());
177 // Not currently expected, so we assert here.
178 MOZ_ASSERT(start
!= "NaN");
181 Maybe
<double> numEnd
= Nothing();
182 if (end
== "Infinity" || end
== "+Infinity") {
183 numEnd
.emplace(PositiveInfinity
<double>());
184 } else if (end
== "-Infinity") {
185 numEnd
.emplace(NegativeInfinity
<double>());
187 // Not currently expected, so we assert here.
188 MOZ_ASSERT(end
!= "NaN");
191 bool isNegativeStart
= !start
.empty() && start
[0] == '-';
192 bool isNegativeEnd
= !end
.empty() && end
[0] == '-';
194 return formatResultToParts(numStart
, isNegativeStart
, numEnd
, isNegativeEnd
,
199 * Formats the number range and selects the keyword by using a provided
200 * UPluralRules object.
202 * https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.selectrange
204 * TODO(1713917) This is necessary because both PluralRules and
205 * NumberRangeFormat have a shared dependency on the raw UFormattedNumberRange
206 * type. Once we transition to using ICU4X, the FFI calls should no
207 * longer require such shared dependencies. At that time, this
208 * functionality should be removed from NumberRangeFormat and invoked
209 * solely from PluralRules.
211 Result
<int32_t, ICUError
> selectForRange(
212 double start
, double end
, char16_t
* keyword
, int32_t keywordSize
,
213 const UPluralRules
* pluralRules
) const;
216 UNumberRangeFormatter
* mNumberRangeFormatter
= nullptr;
217 UFormattedNumberRange
* mFormattedNumberRange
= nullptr;
218 bool mFormatForUnit
= false;
220 Result
<Ok
, ICUError
> initialize(std::string_view aLocale
,
221 const NumberRangeFormatOptions
& aOptions
);
223 [[nodiscard
]] bool formatInternal(double start
, double end
) const;
225 [[nodiscard
]] bool formatInternal(std::string_view start
,
226 std::string_view end
) const;
228 Result
<std::u16string_view
, ICUError
> formatResult() const;
230 Result
<std::u16string_view
, ICUError
> formatResultToParts(
231 Maybe
<double> start
, bool startIsNegative
, Maybe
<double> end
,
232 bool endIsNegative
, NumberPartVector
& parts
) const;
235 } // namespace mozilla::intl