/* 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, ICUError> ListFormat::TryCreate( mozilla::Span 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(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 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(beginIndexInt); size_t endIndex = AssertedCast(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