ListFormat.cpp (3855B)
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 #include "mozilla/intl/ListFormat.h" 5 6 #include "ScopedICUObject.h" 7 8 namespace mozilla::intl { 9 10 /*static*/ Result<UniquePtr<ListFormat>, ICUError> ListFormat::TryCreate( 11 mozilla::Span<const char> aLocale, const Options& aOptions) { 12 UListFormatterType utype = ToUListFormatterType(aOptions.mType); 13 UListFormatterWidth uwidth = ToUListFormatterWidth(aOptions.mStyle); 14 15 UErrorCode status = U_ZERO_ERROR; 16 UListFormatter* fmt = 17 ulistfmt_openForType(IcuLocale(aLocale), utype, uwidth, &status); 18 if (U_FAILURE(status)) { 19 return Err(ICUError::InternalError); 20 } 21 22 return UniquePtr<ListFormat>(new ListFormat(fmt)); 23 } 24 25 ListFormat::~ListFormat() { 26 if (mListFormatter) { 27 ulistfmt_close(mListFormatter.GetMut()); 28 } 29 } 30 31 /* static */ UListFormatterType ListFormat::ToUListFormatterType(Type type) { 32 switch (type) { 33 case Type::Conjunction: 34 return ULISTFMT_TYPE_AND; 35 case Type::Disjunction: 36 return ULISTFMT_TYPE_OR; 37 case Type::Unit: 38 return ULISTFMT_TYPE_UNITS; 39 } 40 MOZ_ASSERT_UNREACHABLE(); 41 return ULISTFMT_TYPE_AND; 42 } 43 44 /* static */ UListFormatterWidth ListFormat::ToUListFormatterWidth( 45 Style style) { 46 switch (style) { 47 case Style::Long: 48 return ULISTFMT_WIDTH_WIDE; 49 case Style::Short: 50 return ULISTFMT_WIDTH_SHORT; 51 case Style::Narrow: 52 return ULISTFMT_WIDTH_NARROW; 53 } 54 MOZ_ASSERT_UNREACHABLE(); 55 return ULISTFMT_WIDTH_WIDE; 56 } 57 58 ICUResult ListFormat::FormattedToParts(const UFormattedValue* formattedValue, 59 size_t formattedSize, 60 PartVector& parts) { 61 size_t lastEndIndex = 0; 62 63 auto AppendPart = [&](PartType type, size_t endIndex) { 64 if (!parts.emplaceBack(type, endIndex)) { 65 return false; 66 } 67 68 lastEndIndex = endIndex; 69 return true; 70 }; 71 72 UErrorCode status = U_ZERO_ERROR; 73 UConstrainedFieldPosition* fpos = ucfpos_open(&status); 74 if (U_FAILURE(status)) { 75 return Err(ICUError::InternalError); 76 } 77 ScopedICUObject<UConstrainedFieldPosition, ucfpos_close> toCloseFpos(fpos); 78 79 // We're only interested in ULISTFMT_ELEMENT_FIELD fields. 80 ucfpos_constrainField(fpos, UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 81 &status); 82 if (U_FAILURE(status)) { 83 return Err(ICUError::InternalError); 84 } 85 86 while (true) { 87 bool hasMore = ufmtval_nextPosition(formattedValue, fpos, &status); 88 if (U_FAILURE(status)) { 89 return Err(ICUError::InternalError); 90 } 91 if (!hasMore) { 92 break; 93 } 94 95 int32_t beginIndexInt, endIndexInt; 96 ucfpos_getIndexes(fpos, &beginIndexInt, &endIndexInt, &status); 97 if (U_FAILURE(status)) { 98 return Err(ICUError::InternalError); 99 } 100 101 MOZ_ASSERT(beginIndexInt <= endIndexInt, 102 "field iterator returning invalid range"); 103 104 size_t beginIndex = AssertedCast<size_t>(beginIndexInt); 105 size_t endIndex = AssertedCast<size_t>(endIndexInt); 106 107 // Indices are guaranteed to be returned in order (from left to right). 108 MOZ_ASSERT(lastEndIndex <= beginIndex, 109 "field iteration didn't return fields in order start to " 110 "finish as expected"); 111 112 if (lastEndIndex < beginIndex) { 113 if (!AppendPart(PartType::Literal, beginIndex)) { 114 return Err(ICUError::InternalError); 115 } 116 } 117 118 if (!AppendPart(PartType::Element, endIndex)) { 119 return Err(ICUError::InternalError); 120 } 121 } 122 123 // Append any final literal. 124 if (lastEndIndex < formattedSize) { 125 if (!AppendPart(PartType::Literal, formattedSize)) { 126 return Err(ICUError::InternalError); 127 } 128 } 129 130 return Ok(); 131 } 132 } // namespace mozilla::intl