diff options
Diffstat (limited to 'intl/components/src/ListFormat.cpp')
-rw-r--r-- | intl/components/src/ListFormat.cpp | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/intl/components/src/ListFormat.cpp b/intl/components/src/ListFormat.cpp new file mode 100644 index 0000000000..6d1e10826a --- /dev/null +++ b/intl/components/src/ListFormat.cpp @@ -0,0 +1,132 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mozilla/intl/ListFormat.h" + +#include "ScopedICUObject.h" + +namespace mozilla::intl { + +/*static*/ Result<UniquePtr<ListFormat>, ICUError> ListFormat::TryCreate( + mozilla::Span<const char> aLocale, const Options& aOptions) { + UListFormatterType utype = ToUListFormatterType(aOptions.mType); + UListFormatterWidth uwidth = ToUListFormatterWidth(aOptions.mStyle); + + UErrorCode status = U_ZERO_ERROR; + UListFormatter* fmt = + ulistfmt_openForType(IcuLocale(aLocale), utype, uwidth, &status); + if (U_FAILURE(status)) { + return Err(ICUError::InternalError); + } + + return UniquePtr<ListFormat>(new ListFormat(fmt)); +} + +ListFormat::~ListFormat() { + if (mListFormatter) { + ulistfmt_close(mListFormatter.GetMut()); + } +} + +/* static */ UListFormatterType ListFormat::ToUListFormatterType(Type type) { + switch (type) { + case Type::Conjunction: + return ULISTFMT_TYPE_AND; + case Type::Disjunction: + return ULISTFMT_TYPE_OR; + case Type::Unit: + return ULISTFMT_TYPE_UNITS; + } + MOZ_ASSERT_UNREACHABLE(); + return ULISTFMT_TYPE_AND; +} + +/* static */ UListFormatterWidth ListFormat::ToUListFormatterWidth( + Style style) { + switch (style) { + case Style::Long: + return ULISTFMT_WIDTH_WIDE; + case Style::Short: + return ULISTFMT_WIDTH_SHORT; + case Style::Narrow: + return ULISTFMT_WIDTH_NARROW; + } + MOZ_ASSERT_UNREACHABLE(); + return ULISTFMT_WIDTH_WIDE; +} + +ICUResult ListFormat::FormattedToParts(const UFormattedValue* formattedValue, + size_t formattedSize, + PartVector& parts) { + size_t lastEndIndex = 0; + + auto AppendPart = [&](PartType type, size_t endIndex) { + if (!parts.emplaceBack(type, endIndex)) { + return false; + } + + lastEndIndex = endIndex; + return true; + }; + + UErrorCode status = U_ZERO_ERROR; + UConstrainedFieldPosition* fpos = ucfpos_open(&status); + if (U_FAILURE(status)) { + return Err(ICUError::InternalError); + } + ScopedICUObject<UConstrainedFieldPosition, ucfpos_close> toCloseFpos(fpos); + + // We're only interested in ULISTFMT_ELEMENT_FIELD fields. + ucfpos_constrainField(fpos, UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, + &status); + if (U_FAILURE(status)) { + return Err(ICUError::InternalError); + } + + while (true) { + bool hasMore = ufmtval_nextPosition(formattedValue, fpos, &status); + if (U_FAILURE(status)) { + return Err(ICUError::InternalError); + } + if (!hasMore) { + break; + } + + int32_t beginIndexInt, endIndexInt; + ucfpos_getIndexes(fpos, &beginIndexInt, &endIndexInt, &status); + if (U_FAILURE(status)) { + return Err(ICUError::InternalError); + } + + MOZ_ASSERT(beginIndexInt <= endIndexInt, + "field iterator returning invalid range"); + + size_t beginIndex = AssertedCast<size_t>(beginIndexInt); + size_t endIndex = AssertedCast<size_t>(endIndexInt); + + // Indices are guaranteed to be returned in order (from left to right). + MOZ_ASSERT(lastEndIndex <= beginIndex, + "field iteration didn't return fields in order start to " + "finish as expected"); + + if (lastEndIndex < beginIndex) { + if (!AppendPart(PartType::Literal, beginIndex)) { + return Err(ICUError::InternalError); + } + } + + if (!AppendPart(PartType::Element, endIndex)) { + return Err(ICUError::InternalError); + } + } + + // Append any final literal. + if (lastEndIndex < formattedSize) { + if (!AppendPart(PartType::Literal, formattedSize)) { + return Err(ICUError::InternalError); + } + } + + return Ok(); +} +} // namespace mozilla::intl |