@@ -14,7 +14,7 @@ use zerovec::{VarZeroVec, ZeroMap, ZeroVec};
1414use icu_pattern:: DoublePlaceholderPattern ;
1515
1616use crate :: dimension:: currency:: CurrencyCode ;
17- use crate :: dimension:: currency:: options:: Width ;
17+ use crate :: dimension:: currency:: options:: { CurrencyUsage , Width } ;
1818
1919#[ cfg( feature = "compiled_data" ) ]
2020/// Baked data
@@ -144,17 +144,26 @@ pub struct CurrencyPatternConfig {
144144 pub narrow_placeholder_value : Option < PlaceholderValue > ,
145145}
146146
147+ // Fallback pattern: "{0} {1}" (number followed by currency with space, e.g. "10 $")
148+ //
149+ // Even though the baked data handles the fallback at data generation time,
150+ // we have the fallback here for users feeding their own data without
151+ // handling the fallback logic in their data generation.
152+ const FALLBACK_PATTERN : & DoublePlaceholderPattern =
153+ DoublePlaceholderPattern :: from_ref_store_unchecked ( "\x02 \x05 " ) ;
154+
147155impl < ' a > CurrencyEssentials < ' a > {
148156 /// Returns the formatted currency name/symbol,
149157 /// the currency pattern for the given width and currency,
150158 /// and the pattern selection.
151159 pub ( crate ) fn name_and_pattern (
152160 & ' a self ,
153161 width : Width ,
162+ usage : CurrencyUsage ,
154163 currency : & ' a CurrencyCode ,
155164 ) -> (
156165 & ' a str ,
157- Option < & ' a DoublePlaceholderPattern > ,
166+ & ' a DoublePlaceholderPattern ,
158167 Option < & ' a DoublePlaceholderPattern > ,
159168 PatternSelection ,
160169 ) {
@@ -179,16 +188,28 @@ impl<'a> CurrencyEssentials<'a> {
179188 Width :: Narrow => config. narrow_pattern_selection ,
180189 } ;
181190
182- let pos_pattern = match pattern_selection {
183- PatternSelection :: Standard => self . standard_pattern ( ) ,
184- PatternSelection :: StandardAlphaNextToNumber => {
191+ let pos_pattern = match ( usage, pattern_selection) {
192+ ( CurrencyUsage :: Accounting , PatternSelection :: Standard ) => {
193+ self . accounting_positive_pattern ( )
194+ }
195+ ( CurrencyUsage :: Accounting , PatternSelection :: StandardAlphaNextToNumber ) => {
196+ self . accounting_alpha_next_to_number_positive_pattern ( )
197+ }
198+ ( _, PatternSelection :: Standard ) => self . standard_pattern ( ) ,
199+ ( _, PatternSelection :: StandardAlphaNextToNumber ) => {
185200 self . standard_alpha_next_to_number_pattern ( )
186201 }
187202 } ;
188203
189- let neg_pattern = match pattern_selection {
190- PatternSelection :: Standard => self . standard_negative_pattern ( ) ,
191- PatternSelection :: StandardAlphaNextToNumber => {
204+ let neg_pattern = match ( usage, pattern_selection) {
205+ ( CurrencyUsage :: Accounting , PatternSelection :: Standard ) => {
206+ self . accounting_negative_pattern ( )
207+ }
208+ ( CurrencyUsage :: Accounting , PatternSelection :: StandardAlphaNextToNumber ) => {
209+ self . accounting_alpha_next_to_number_negative_pattern ( )
210+ }
211+ ( _, PatternSelection :: Standard ) => self . standard_negative_pattern ( ) ,
212+ ( _, PatternSelection :: StandardAlphaNextToNumber ) => {
192213 self . standard_alpha_next_to_number_negative_pattern ( )
193214 }
194215 } ;
@@ -197,10 +218,23 @@ impl<'a> CurrencyEssentials<'a> {
197218 }
198219
199220 /// Returns the standard pattern.
200- pub fn standard_pattern ( & self ) -> Option < & DoublePlaceholderPattern > {
221+ ///
222+ /// If the standard pattern is missing (which should only happen in corrupt
223+ /// or incomplete custom data), this returns a default safety pattern `"{1}{0}"`.
224+ /// Note that this is a safety default, not a CLDR-defined fallback.
225+ pub fn standard_pattern ( & self ) -> & DoublePlaceholderPattern {
201226 self . patterns
202227 . get ( self . indices . standard as usize )
203- . or_else ( || self . patterns . get ( 0 ) )
228+ . unwrap_or_else ( || {
229+ debug_assert ! (
230+ false ,
231+ "Standard pattern index {} is out of bounds for patterns of length {}" ,
232+ self . indices. standard,
233+ self . patterns. len( )
234+ ) ;
235+ // GIGO
236+ FALLBACK_PATTERN
237+ } )
204238 }
205239
206240 /// Returns the standard negative pattern if specified.
@@ -210,14 +244,24 @@ impl<'a> CurrencyEssentials<'a> {
210244 . and_then ( |idx| self . patterns . get ( idx as usize ) )
211245 }
212246
213- /// Returns the `standard_alpha_next_to_number` pattern, falling back to `standard_pattern` if not present.
214- pub fn standard_alpha_next_to_number_pattern ( & self ) -> Option < & DoublePlaceholderPattern > {
247+ /// Returns the `standard_alpha_next_to_number` pattern.
248+ ///
249+ /// Fallback hierarchy:
250+ /// `standard_alpha_next_to_number` -> `standard`
251+ ///
252+ /// Even though the baked data handles the fallback at data generation time,
253+ /// we have the fallback here for users feeding their own data without
254+ /// handling the fallback logic in their data generation.
255+ pub fn standard_alpha_next_to_number_pattern ( & self ) -> & DoublePlaceholderPattern {
215256 self . patterns
216257 . get ( self . indices . standard_alpha_next_to_number as usize )
217- . or_else ( || self . standard_pattern ( ) )
258+ . unwrap_or_else ( || self . standard_pattern ( ) )
218259 }
219260
220261 /// Returns the `standard_alpha_next_to_number` negative pattern if specified, falling back to standard negative.
262+ ///
263+ /// Fallback hierarchy:
264+ /// `standard_alpha_next_to_number_negative` -> `standard_negative`
221265 pub fn standard_alpha_next_to_number_negative_pattern (
222266 & self ,
223267 ) -> Option < & DoublePlaceholderPattern > {
@@ -227,30 +271,49 @@ impl<'a> CurrencyEssentials<'a> {
227271 . or_else ( || self . standard_negative_pattern ( ) )
228272 }
229273
230- /// Returns the positive accounting pattern, falling back to `standard_pattern` if not present.
231- pub fn accounting_positive_pattern ( & self ) -> Option < & DoublePlaceholderPattern > {
274+ /// Returns the positive accounting pattern.
275+ ///
276+ /// Fallback hierarchy:
277+ /// `accounting_positive` -> `standard`
278+ ///
279+ /// Even though the baked data handles the fallback at data generation time,
280+ /// we have the fallback here for users feeding their own data without
281+ /// handling the fallback logic in their data generation.
282+ pub fn accounting_positive_pattern ( & self ) -> & DoublePlaceholderPattern {
232283 self . patterns
233284 . get ( self . indices . accounting_positive as usize )
234- . or_else ( || self . standard_pattern ( ) )
285+ . unwrap_or_else ( || self . standard_pattern ( ) )
235286 }
236287
237- /// Returns the negative accounting pattern if present.
288+ /// Returns the negative accounting pattern if present, falling back to standard negative.
289+ ///
290+ /// Fallback hierarchy:
291+ /// `accounting_negative` -> `standard_negative`
238292 pub fn accounting_negative_pattern ( & self ) -> Option < & DoublePlaceholderPattern > {
239293 self . indices
240294 . accounting_negative
241295 . and_then ( |idx| self . patterns . get ( idx as usize ) )
296+ . or_else ( || self . standard_negative_pattern ( ) )
242297 }
243298
244- /// Returns the positive `accounting_alpha_next_to_number` pattern, falling back to accounting or standard.
245- pub fn accounting_alpha_next_to_number_positive_pattern (
246- & self ,
247- ) -> Option < & DoublePlaceholderPattern > {
299+ /// Returns the positive `accounting_alpha_next_to_number` pattern.
300+ ///
301+ /// Fallback hierarchy:
302+ /// `accounting_alpha_next_to_number_positive` -> `standard_alpha_next_to_number` -> `standard`
303+ ///
304+ /// Even though the baked data handles the fallback at data generation time,
305+ /// we have the fallback here for users feeding their own data without
306+ /// handling the fallback logic in their data generation.
307+ pub fn accounting_alpha_next_to_number_positive_pattern ( & self ) -> & DoublePlaceholderPattern {
248308 self . patterns
249309 . get ( self . indices . accounting_alpha_next_to_number_positive as usize )
250- . or_else ( || self . accounting_positive_pattern ( ) )
310+ . unwrap_or_else ( || self . standard_alpha_next_to_number_pattern ( ) )
251311 }
252312
253313 /// Returns the negative `accounting_alpha_next_to_number` pattern, falling back to `accounting_negative_pattern`.
314+ ///
315+ /// Fallback hierarchy:
316+ /// `accounting_alpha_next_to_number_negative` -> `accounting_negative` -> `standard_negative`
254317 pub fn accounting_alpha_next_to_number_negative_pattern (
255318 & self ,
256319 ) -> Option < & DoublePlaceholderPattern > {
@@ -260,3 +323,14 @@ impl<'a> CurrencyEssentials<'a> {
260323 . or_else ( || self . accounting_negative_pattern ( ) )
261324 }
262325}
326+
327+ #[ cfg( test) ]
328+ mod tests {
329+ use super :: * ;
330+ use writeable:: assert_writeable_eq;
331+
332+ #[ test]
333+ fn test_fallback_pattern ( ) {
334+ assert_writeable_eq ! ( FALLBACK_PATTERN . interpolate( ( "10" , "$" ) ) , "10 $" ) ;
335+ }
336+ }
0 commit comments