@@ -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 ) {
@@ -181,16 +190,28 @@ impl<'a> CurrencyEssentials<'a> {
181190 Width :: Long => unreachable ! ( "CurrencyEssentials does not support Long width" ) ,
182191 } ;
183192
184- let pos_pattern = match pattern_selection {
185- PatternSelection :: Standard => self . standard_pattern ( ) ,
186- PatternSelection :: StandardAlphaNextToNumber => {
193+ let pos_pattern = match ( usage, pattern_selection) {
194+ ( CurrencyUsage :: Accounting , PatternSelection :: Standard ) => {
195+ self . accounting_positive_pattern ( )
196+ }
197+ ( CurrencyUsage :: Accounting , PatternSelection :: StandardAlphaNextToNumber ) => {
198+ self . accounting_alpha_next_to_number_positive_pattern ( )
199+ }
200+ ( _, PatternSelection :: Standard ) => self . standard_pattern ( ) ,
201+ ( _, PatternSelection :: StandardAlphaNextToNumber ) => {
187202 self . standard_alpha_next_to_number_pattern ( )
188203 }
189204 } ;
190205
191- let neg_pattern = match pattern_selection {
192- PatternSelection :: Standard => self . standard_negative_pattern ( ) ,
193- PatternSelection :: StandardAlphaNextToNumber => {
206+ let neg_pattern = match ( usage, pattern_selection) {
207+ ( CurrencyUsage :: Accounting , PatternSelection :: Standard ) => {
208+ self . accounting_negative_pattern ( )
209+ }
210+ ( CurrencyUsage :: Accounting , PatternSelection :: StandardAlphaNextToNumber ) => {
211+ self . accounting_alpha_next_to_number_negative_pattern ( )
212+ }
213+ ( _, PatternSelection :: Standard ) => self . standard_negative_pattern ( ) ,
214+ ( _, PatternSelection :: StandardAlphaNextToNumber ) => {
194215 self . standard_alpha_next_to_number_negative_pattern ( )
195216 }
196217 } ;
@@ -199,10 +220,23 @@ impl<'a> CurrencyEssentials<'a> {
199220 }
200221
201222 /// Returns the standard pattern.
202- pub fn standard_pattern ( & self ) -> Option < & DoublePlaceholderPattern > {
223+ ///
224+ /// If the standard pattern is missing (which should only happen in corrupt
225+ /// or incomplete custom data), this returns a default safety pattern `"{1}{0}"`.
226+ /// Note that this is a safety default, not a CLDR-defined fallback.
227+ pub fn standard_pattern ( & self ) -> & DoublePlaceholderPattern {
203228 self . patterns
204229 . get ( self . indices . standard as usize )
205- . or_else ( || self . patterns . get ( 0 ) )
230+ . unwrap_or_else ( || {
231+ debug_assert ! (
232+ false ,
233+ "Standard pattern index {} is out of bounds for patterns of length {}" ,
234+ self . indices. standard,
235+ self . patterns. len( )
236+ ) ;
237+ // GIGO
238+ FALLBACK_PATTERN
239+ } )
206240 }
207241
208242 /// Returns the standard negative pattern if specified.
@@ -212,14 +246,24 @@ impl<'a> CurrencyEssentials<'a> {
212246 . and_then ( |idx| self . patterns . get ( idx as usize ) )
213247 }
214248
215- /// Returns the `standard_alpha_next_to_number` pattern, falling back to `standard_pattern` if not present.
216- pub fn standard_alpha_next_to_number_pattern ( & self ) -> Option < & DoublePlaceholderPattern > {
249+ /// Returns the `standard_alpha_next_to_number` pattern.
250+ ///
251+ /// Fallback hierarchy:
252+ /// `standard_alpha_next_to_number` -> `standard`
253+ ///
254+ /// Even though the baked data handles the fallback at data generation time,
255+ /// we have the fallback here for users feeding their own data without
256+ /// handling the fallback logic in their data generation.
257+ pub fn standard_alpha_next_to_number_pattern ( & self ) -> & DoublePlaceholderPattern {
217258 self . patterns
218259 . get ( self . indices . standard_alpha_next_to_number as usize )
219- . or_else ( || self . standard_pattern ( ) )
260+ . unwrap_or_else ( || self . standard_pattern ( ) )
220261 }
221262
222263 /// Returns the `standard_alpha_next_to_number` negative pattern if specified, falling back to standard negative.
264+ ///
265+ /// Fallback hierarchy:
266+ /// `standard_alpha_next_to_number_negative` -> `standard_negative`
223267 pub fn standard_alpha_next_to_number_negative_pattern (
224268 & self ,
225269 ) -> Option < & DoublePlaceholderPattern > {
@@ -229,30 +273,49 @@ impl<'a> CurrencyEssentials<'a> {
229273 . or_else ( || self . standard_negative_pattern ( ) )
230274 }
231275
232- /// Returns the positive accounting pattern, falling back to `standard_pattern` if not present.
233- pub fn accounting_positive_pattern ( & self ) -> Option < & DoublePlaceholderPattern > {
276+ /// Returns the positive accounting pattern.
277+ ///
278+ /// Fallback hierarchy:
279+ /// `accounting_positive` -> `standard`
280+ ///
281+ /// Even though the baked data handles the fallback at data generation time,
282+ /// we have the fallback here for users feeding their own data without
283+ /// handling the fallback logic in their data generation.
284+ pub fn accounting_positive_pattern ( & self ) -> & DoublePlaceholderPattern {
234285 self . patterns
235286 . get ( self . indices . accounting_positive as usize )
236- . or_else ( || self . standard_pattern ( ) )
287+ . unwrap_or_else ( || self . standard_pattern ( ) )
237288 }
238289
239- /// Returns the negative accounting pattern if present.
290+ /// Returns the negative accounting pattern if present, falling back to standard negative.
291+ ///
292+ /// Fallback hierarchy:
293+ /// `accounting_negative` -> `standard_negative`
240294 pub fn accounting_negative_pattern ( & self ) -> Option < & DoublePlaceholderPattern > {
241295 self . indices
242296 . accounting_negative
243297 . and_then ( |idx| self . patterns . get ( idx as usize ) )
298+ . or_else ( || self . standard_negative_pattern ( ) )
244299 }
245300
246- /// Returns the positive `accounting_alpha_next_to_number` pattern, falling back to accounting or standard.
247- pub fn accounting_alpha_next_to_number_positive_pattern (
248- & self ,
249- ) -> Option < & DoublePlaceholderPattern > {
301+ /// Returns the positive `accounting_alpha_next_to_number` pattern.
302+ ///
303+ /// Fallback hierarchy:
304+ /// `accounting_alpha_next_to_number_positive` -> `standard_alpha_next_to_number` -> `standard`
305+ ///
306+ /// Even though the baked data handles the fallback at data generation time,
307+ /// we have the fallback here for users feeding their own data without
308+ /// handling the fallback logic in their data generation.
309+ pub fn accounting_alpha_next_to_number_positive_pattern ( & self ) -> & DoublePlaceholderPattern {
250310 self . patterns
251311 . get ( self . indices . accounting_alpha_next_to_number_positive as usize )
252- . or_else ( || self . accounting_positive_pattern ( ) )
312+ . unwrap_or_else ( || self . standard_alpha_next_to_number_pattern ( ) )
253313 }
254314
255315 /// Returns the negative `accounting_alpha_next_to_number` pattern, falling back to `accounting_negative_pattern`.
316+ ///
317+ /// Fallback hierarchy:
318+ /// `accounting_alpha_next_to_number_negative` -> `accounting_negative` -> `standard_negative`
256319 pub fn accounting_alpha_next_to_number_negative_pattern (
257320 & self ,
258321 ) -> Option < & DoublePlaceholderPattern > {
@@ -262,3 +325,14 @@ impl<'a> CurrencyEssentials<'a> {
262325 . or_else ( || self . accounting_negative_pattern ( ) )
263326 }
264327}
328+
329+ #[ cfg( test) ]
330+ mod tests {
331+ use super :: * ;
332+ use writeable:: assert_writeable_eq;
333+
334+ #[ test]
335+ fn test_fallback_pattern ( ) {
336+ assert_writeable_eq ! ( FALLBACK_PATTERN . interpolate( ( "10" , "$" ) ) , "10 $" ) ;
337+ }
338+ }
0 commit comments