• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zbraniecki / icu4x / 6815798908

09 Nov 2023 05:17PM UTC coverage: 72.607% (-2.4%) from 75.01%
6815798908

push

github

web-flow
Implement `Any/BufferProvider` for some smart pointers (#4255)

Allows storing them as a `Box<dyn Any/BufferProvider>` without using a
wrapper type that implements the trait.

44281 of 60987 relevant lines covered (72.61%)

201375.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.47
/components/datetime/src/raw/datetime.rs
1
// This file is part of ICU4X. For terms of use, please see the file
×
2
// called LICENSE at the top level of the ICU4X source tree
3
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4

5
//! The collection of code that is needed for handling formatting operations for DateTimes.
6
//! Central to this is the [`DateTimeFormatter`].
7

8
use crate::{
9
    format::datetime,
10
    input::{DateInput, DateTimeInput, ExtractedDateTimeInput, IsoTimeInput},
11
    options::{length, preferences},
12
    pattern::runtime::PatternPlurals,
13
    provider::{
14
        self,
15
        calendar::{
16
            patterns::GenericPatternV1Marker, patterns::PatternPluralsFromPatternsV1Marker,
17
            ErasedDateLengthsV1Marker, ErasedDateSymbolsV1Marker, TimeLengthsV1Marker,
18
            TimeSymbolsV1Marker,
19
        },
20
    },
21
    DateTimeError, FormattedDateTime,
22
};
23

24
use icu_calendar::provider::WeekDataV1Marker;
25
use icu_calendar::week::WeekCalculator;
26
use icu_decimal::{
27
    options::{FixedDecimalFormatterOptions, GroupingStrategy},
28
    provider::DecimalSymbolsV1Marker,
29
    FixedDecimalFormatter,
30
};
31
use icu_plurals::{provider::OrdinalV1Marker, PluralRules};
32
use icu_provider::prelude::*;
33

34
#[derive(Debug)]
×
35
pub(crate) struct TimeFormatter {
36
    pub patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
×
37
    pub symbols: Option<DataPayload<TimeSymbolsV1Marker>>,
×
38
    pub fixed_decimal_format: FixedDecimalFormatter,
×
39
}
40

41
impl TimeFormatter {
42
    #[inline(never)]
43
    #[cfg(feature = "compiled_data")]
44
    pub fn try_new(
22✔
45
        locale: &DataLocale,
46
        length: length::Time,
47
        preferences: Option<preferences::Bag>,
48
    ) -> Result<Self, DateTimeError> {
49
        let patterns = provider::date_time::pattern_for_time_length(
22✔
50
            &crate::provider::Baked,
51
            locale,
52
            length,
53
            preferences,
54
        )?;
×
55

56
        let required = datetime::analyze_patterns(&patterns.get().0, false)
22✔
57
            .map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
×
58

59
        let symbols_data = if required.time_symbols_data {
37✔
60
            Some(
15✔
61
                crate::provider::Baked
15✔
62
                    .load(DataRequest {
15✔
63
                        locale,
64
                        metadata: Default::default(),
15✔
65
                    })?
×
66
                    .take_payload()?,
×
67
            )
68
        } else {
×
69
            None
7✔
70
        };
71

72
        let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
22✔
73
        fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
22✔
74

75
        let fixed_decimal_format =
76
            FixedDecimalFormatter::try_new(locale, fixed_decimal_format_options)?;
22✔
77

78
        Ok(Self::new(patterns, symbols_data, fixed_decimal_format))
22✔
79
    }
22✔
80

81
    #[inline(never)]
82
    pub fn try_new_unstable<D>(
×
83
        provider: &D,
84
        locale: &DataLocale,
85
        length: length::Time,
86
        preferences: Option<preferences::Bag>,
87
    ) -> Result<Self, DateTimeError>
88
    where
89
        D: DataProvider<TimeLengthsV1Marker>
90
            + DataProvider<TimeSymbolsV1Marker>
91
            + DataProvider<DecimalSymbolsV1Marker>
92
            + ?Sized,
93
    {
94
        let patterns =
×
95
            provider::date_time::pattern_for_time_length(provider, locale, length, preferences)?;
×
96

97
        let required = datetime::analyze_patterns(&patterns.get().0, false)
×
98
            .map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
×
99

100
        let symbols_data = if required.time_symbols_data {
×
101
            Some(
×
102
                provider
×
103
                    .load(DataRequest {
×
104
                        locale,
105
                        metadata: Default::default(),
×
106
                    })?
×
107
                    .take_payload()?,
×
108
            )
109
        } else {
×
110
            None
×
111
        };
112

113
        let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
×
114
        fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
×
115

116
        let fixed_decimal_format = FixedDecimalFormatter::try_new_unstable(
×
117
            provider,
118
            locale,
119
            fixed_decimal_format_options,
120
        )?;
×
121

122
        Ok(Self::new(patterns, symbols_data, fixed_decimal_format))
×
123
    }
×
124

125
    /// Creates a new [`TimeFormatter`] regardless of whether there are time-zone symbols in the pattern.
126
    pub fn new(
22✔
127
        patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
128
        symbols: Option<DataPayload<TimeSymbolsV1Marker>>,
129
        fixed_decimal_format: FixedDecimalFormatter,
130
    ) -> Self {
131
        Self {
22✔
132
            patterns,
22✔
133
            symbols,
22✔
134
            fixed_decimal_format,
22✔
135
        }
136
    }
22✔
137

138
    /// Takes a [`IsoTimeInput`] implementer and returns an instance of a [`FormattedDateTime`]
139
    /// that contains all information necessary to display a formatted date and operate on it.
140
    #[inline]
141
    pub fn format<'l, T>(&'l self, value: &T) -> FormattedDateTime<'l>
×
142
    where
143
        T: IsoTimeInput,
144
    {
145
        FormattedDateTime {
×
146
            patterns: &self.patterns,
×
147
            date_symbols: None,
×
148
            time_symbols: self.symbols.as_ref().map(|s| s.get()),
×
149
            datetime: ExtractedDateTimeInput::extract_from_time(value),
×
150
            week_data: None,
151
            ordinal_rules: None,
152
            fixed_decimal_format: &self.fixed_decimal_format,
×
153
        }
154
    }
×
155
}
156

157
#[derive(Debug)]
×
158
pub(crate) struct DateFormatter {
159
    pub generic_pattern: DataPayload<GenericPatternV1Marker>,
×
160
    pub patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
×
161
    pub symbols: Option<DataPayload<ErasedDateSymbolsV1Marker>>,
×
162
    pub week_data: Option<WeekCalculator>,
×
163
    pub ordinal_rules: Option<PluralRules>,
×
164
    pub fixed_decimal_format: FixedDecimalFormatter,
×
165
}
166

167
impl DateFormatter {
168
    #[cfg(feature = "compiled_data")]
169
    #[inline(never)]
170
    pub fn try_new(
4✔
171
        patterns_data: DataPayload<ErasedDateLengthsV1Marker>,
172
        symbols_data_fn: impl FnOnce() -> Result<DataPayload<ErasedDateSymbolsV1Marker>, DataError>,
173
        locale: &DataLocale,
174
        length: length::Date,
175
    ) -> Result<Self, DateTimeError> {
176
        let patterns = provider::date_time::pattern_for_date_length(length, patterns_data.clone());
4✔
177

178
        let generic_pattern =
179
            provider::date_time::generic_pattern_for_date_length(length, patterns_data);
4✔
180

181
        let required = datetime::analyze_patterns(&patterns.get().0, false)
4✔
182
            .map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
×
183

184
        let week_data = if required.week_data {
4✔
185
            Some(icu_calendar::week::WeekCalculator::try_new(locale)?)
×
186
        } else {
187
            None
4✔
188
        };
189

190
        let ordinal_rules = if let PatternPlurals::MultipleVariants(_) = &patterns.get().0 {
4✔
191
            Some(PluralRules::try_new_ordinal(locale)?)
×
192
        } else {
×
193
            None
4✔
194
        };
195

196
        let symbols_data = if required.date_symbols_data {
8✔
197
            Some(symbols_data_fn()?)
4✔
198
        } else {
×
199
            None
×
200
        };
201

202
        let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
4✔
203
        fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
4✔
204

205
        let fixed_decimal_format =
206
            FixedDecimalFormatter::try_new(locale, fixed_decimal_format_options)?;
4✔
207

208
        Ok(Self::new(
4✔
209
            generic_pattern,
4✔
210
            patterns,
4✔
211
            symbols_data,
4✔
212
            week_data,
4✔
213
            ordinal_rules,
4✔
214
            fixed_decimal_format,
4✔
215
        ))
216
    }
4✔
217

218
    #[inline(never)]
219
    pub fn try_new_unstable<D>(
1✔
220
        provider: &D,
221
        patterns_data: DataPayload<ErasedDateLengthsV1Marker>,
222
        symbols_data_fn: impl FnOnce() -> Result<DataPayload<ErasedDateSymbolsV1Marker>, DataError>,
223
        locale: &DataLocale,
224
        length: length::Date,
225
    ) -> Result<Self, DateTimeError>
226
    where
227
        D: DataProvider<DecimalSymbolsV1Marker>
228
            + DataProvider<OrdinalV1Marker>
229
            + DataProvider<WeekDataV1Marker>
230
            + ?Sized,
231
    {
232
        let patterns = provider::date_time::pattern_for_date_length(length, patterns_data.clone());
1✔
233

234
        let generic_pattern =
235
            provider::date_time::generic_pattern_for_date_length(length, patterns_data);
1✔
236

237
        let required = datetime::analyze_patterns(&patterns.get().0, false)
1✔
238
            .map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
×
239

240
        let week_data = if required.week_data {
1✔
241
            Some(icu_calendar::week::WeekCalculator::try_new_unstable(
×
242
                provider, locale,
243
            )?)
×
244
        } else {
245
            None
1✔
246
        };
247

248
        let ordinal_rules = if let PatternPlurals::MultipleVariants(_) = &patterns.get().0 {
1✔
249
            Some(PluralRules::try_new_ordinal_unstable(provider, locale)?)
×
250
        } else {
×
251
            None
1✔
252
        };
253

254
        let symbols_data = if required.date_symbols_data {
2✔
255
            Some(symbols_data_fn()?)
1✔
256
        } else {
×
257
            None
×
258
        };
259

260
        let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
1✔
261
        fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
1✔
262

263
        let fixed_decimal_format = FixedDecimalFormatter::try_new_unstable(
1✔
264
            provider,
265
            locale,
266
            fixed_decimal_format_options,
267
        )?;
×
268

269
        Ok(Self::new(
1✔
270
            generic_pattern,
1✔
271
            patterns,
1✔
272
            symbols_data,
1✔
273
            week_data,
1✔
274
            ordinal_rules,
1✔
275
            fixed_decimal_format,
1✔
276
        ))
277
    }
1✔
278

279
    /// Creates a new [`DateTimeFormatter`] regardless of whether there are time-zone symbols in the pattern.
280
    pub fn new(
30✔
281
        generic_pattern: DataPayload<GenericPatternV1Marker>,
282
        patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
283
        symbols: Option<DataPayload<ErasedDateSymbolsV1Marker>>,
284
        week_data: Option<WeekCalculator>,
285
        ordinal_rules: Option<PluralRules>,
286
        fixed_decimal_format: FixedDecimalFormatter,
287
    ) -> Self {
288
        Self {
30✔
289
            generic_pattern,
30✔
290
            patterns,
30✔
291
            symbols,
30✔
292
            week_data,
293
            ordinal_rules,
30✔
294
            fixed_decimal_format,
30✔
295
        }
296
    }
30✔
297

298
    /// Takes a [`DateInput`] implementer and returns an instance of a [`FormattedDateTime`]
299
    /// that contains all information necessary to display a formatted date and operate on it.
300
    #[inline]
301
    pub fn format<'l, T>(&'l self, value: &T) -> FormattedDateTime<'l>
2✔
302
    where
303
        T: DateInput,
304
    {
305
        FormattedDateTime {
2✔
306
            patterns: &self.patterns,
2✔
307
            date_symbols: self.symbols.as_ref().map(|s| s.get()),
4✔
308
            time_symbols: None,
2✔
309
            datetime: ExtractedDateTimeInput::extract_from_date(value),
2✔
310
            week_data: None,
311
            ordinal_rules: None,
312
            fixed_decimal_format: &self.fixed_decimal_format,
2✔
313
        }
314
    }
2✔
315
}
316

317
/// This is the internal "raw" version of [crate::DateTimeFormatter], i.e. a version of DateTimeFormatter
318
/// without the generic parameter. The actual implementation of [crate::DateTimeFormatter] should live here.
319
#[derive(Debug)]
×
320
pub(crate) struct DateTimeFormatter {
321
    pub patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
×
322
    pub date_symbols: Option<DataPayload<ErasedDateSymbolsV1Marker>>,
×
323
    pub time_symbols: Option<DataPayload<TimeSymbolsV1Marker>>,
×
324
    pub week_data: Option<WeekCalculator>,
×
325
    pub ordinal_rules: Option<PluralRules>,
×
326
    pub fixed_decimal_format: FixedDecimalFormatter,
×
327
}
328

329
impl DateTimeFormatter {
330
    #[inline(never)]
331
    pub fn try_from_date_and_time(
17✔
332
        date: DateFormatter,
333
        time: TimeFormatter,
334
    ) -> Result<Self, DateTimeError> {
335
        let generic_pattern = &date.generic_pattern;
17✔
336
        let time_patterns = &time.patterns;
17✔
337
        let patterns = date
34✔
338
            .patterns
339
            .try_map_project::<PatternPluralsFromPatternsV1Marker, _, DateTimeError>(
340
                |data, _| {
34✔
341
                    let date_pattern = data.0.expect_pattern("Lengths are single patterns");
17✔
342
                    let time_pattern: crate::pattern::runtime::Pattern = time_patterns
17✔
343
                        .get()
344
                        .clone()
345
                        .0
346
                        .expect_pattern("Lengths are single patterns");
347

348
                    Ok(PatternPlurals::from(
17✔
349
                        generic_pattern
17✔
350
                            .get()
351
                            .clone()
352
                            .0
353
                            .combined(date_pattern, time_pattern)?,
17✔
354
                    )
355
                    .into())
356
                },
17✔
357
            )?;
×
358

359
        Ok(Self {
17✔
360
            patterns,
17✔
361
            date_symbols: date.symbols,
17✔
362
            time_symbols: time.symbols,
17✔
363
            week_data: date.week_data,
17✔
364
            ordinal_rules: date.ordinal_rules,
17✔
365
            fixed_decimal_format: date.fixed_decimal_format,
17✔
366
        })
367
    }
17✔
368

369
    #[inline(never)]
370
    #[cfg(feature = "compiled_data")]
371
    pub fn try_new(
181✔
372
        patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
373
        symbols_data_fn: impl FnOnce() -> Result<DataPayload<ErasedDateSymbolsV1Marker>, DataError>,
374
        locale: &DataLocale,
375
    ) -> Result<Self, DateTimeError> {
376
        let required = datetime::analyze_patterns(&patterns.get().0, false)
181✔
377
            .map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
×
378

379
        let req = DataRequest {
179✔
380
            locale,
381
            metadata: Default::default(),
179✔
382
        };
383

384
        let week_data = if required.week_data {
191✔
385
            Some(icu_calendar::week::WeekCalculator::try_new(locale)?)
12✔
386
        } else {
387
            None
167✔
388
        };
389

390
        let ordinal_rules = if let PatternPlurals::MultipleVariants(_) = &patterns.get().0 {
182✔
391
            Some(PluralRules::try_new_ordinal(locale)?)
3✔
392
        } else {
×
393
            None
175✔
394
        };
395

396
        let date_symbols_data = if required.date_symbols_data {
307✔
397
            Some(symbols_data_fn()?)
128✔
398
        } else {
×
399
            None
50✔
400
        };
401

402
        let time_symbols_data = if required.time_symbols_data {
206✔
403
            Some(crate::provider::Baked.load(req)?.take_payload()?)
27✔
404
        } else {
×
405
            None
152✔
406
        };
407

408
        let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
179✔
409
        fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
179✔
410

411
        let fixed_decimal_format =
412
            FixedDecimalFormatter::try_new(locale, fixed_decimal_format_options)?;
179✔
413

414
        Ok(Self::new(
179✔
415
            patterns,
179✔
416
            date_symbols_data,
179✔
417
            time_symbols_data,
179✔
418
            week_data,
179✔
419
            ordinal_rules,
179✔
420
            fixed_decimal_format,
179✔
421
        ))
422
    }
179✔
423

424
    #[inline(never)]
425
    pub fn try_new_unstable<D>(
1✔
426
        provider: &D,
427
        patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
428
        symbols_data_fn: impl FnOnce() -> Result<DataPayload<ErasedDateSymbolsV1Marker>, DataError>,
429
        locale: &DataLocale,
430
    ) -> Result<Self, DateTimeError>
431
    where
432
        D: DataProvider<TimeSymbolsV1Marker>
433
            + DataProvider<TimeLengthsV1Marker>
434
            + DataProvider<DecimalSymbolsV1Marker>
435
            + DataProvider<OrdinalV1Marker>
436
            + DataProvider<WeekDataV1Marker>
437
            + ?Sized,
438
    {
439
        let required = datetime::analyze_patterns(&patterns.get().0, false)
1✔
440
            .map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
×
441

442
        let req = DataRequest {
1✔
443
            locale,
444
            metadata: Default::default(),
1✔
445
        };
446

447
        let week_data = if required.week_data {
1✔
448
            Some(icu_calendar::week::WeekCalculator::try_new_unstable(
×
449
                provider, locale,
450
            )?)
×
451
        } else {
452
            None
1✔
453
        };
454

455
        let ordinal_rules = if let PatternPlurals::MultipleVariants(_) = &patterns.get().0 {
1✔
456
            Some(PluralRules::try_new_ordinal_unstable(provider, locale)?)
×
457
        } else {
×
458
            None
1✔
459
        };
460

461
        let date_symbols_data = if required.date_symbols_data {
2✔
462
            Some(symbols_data_fn()?)
1✔
463
        } else {
×
464
            None
×
465
        };
466

467
        let time_symbols_data = if required.time_symbols_data {
2✔
468
            Some(provider.load(req)?.take_payload()?)
1✔
469
        } else {
×
470
            None
×
471
        };
472

473
        let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
1✔
474
        fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
1✔
475

476
        let fixed_decimal_format = FixedDecimalFormatter::try_new_unstable(
1✔
477
            provider,
478
            locale,
479
            fixed_decimal_format_options,
480
        )?;
×
481

482
        Ok(Self::new(
1✔
483
            patterns,
1✔
484
            date_symbols_data,
1✔
485
            time_symbols_data,
1✔
486
            week_data,
1✔
487
            ordinal_rules,
1✔
488
            fixed_decimal_format,
1✔
489
        ))
490
    }
1✔
491

492
    /// Creates a new [`DateTimeFormatter`] regardless of whether there are time-zone symbols in the pattern.
493
    pub fn new(
646✔
494
        patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
495
        date_symbols: Option<DataPayload<ErasedDateSymbolsV1Marker>>,
496
        time_symbols: Option<DataPayload<TimeSymbolsV1Marker>>,
497
        week_data: Option<WeekCalculator>,
498
        ordinal_rules: Option<PluralRules>,
499
        fixed_decimal_format: FixedDecimalFormatter,
500
    ) -> Self {
501
        Self {
646✔
502
            patterns,
646✔
503
            date_symbols,
646✔
504
            time_symbols,
646✔
505
            week_data,
506
            ordinal_rules,
646✔
507
            fixed_decimal_format,
646✔
508
        }
509
    }
646✔
510

511
    /// Takes a [`DateTimeInput`] implementer and returns an instance of a [`FormattedDateTime`]
512
    /// that contains all information necessary to display a formatted date and operate on it.
513
    #[inline]
514
    pub fn format<'l, T>(&'l self, value: &T) -> FormattedDateTime<'l>
7✔
515
    where
516
        T: DateTimeInput,
517
    {
518
        // Todo: optimize extraction #2143
519
        FormattedDateTime {
7✔
520
            patterns: &self.patterns,
7✔
521
            date_symbols: self.date_symbols.as_ref().map(|s| s.get()),
15✔
522
            time_symbols: self.time_symbols.as_ref().map(|s| s.get()),
8✔
523
            datetime: ExtractedDateTimeInput::extract_from(value),
7✔
524
            week_data: self.week_data.as_ref(),
7✔
525
            ordinal_rules: self.ordinal_rules.as_ref(),
7✔
526
            fixed_decimal_format: &self.fixed_decimal_format,
7✔
527
        }
528
    }
7✔
529

530
    /// Returns a [`components::Bag`](crate::options::components::Bag) that represents the resolved components for the
531
    /// options that were provided to the [`DateTimeFormatter`].
532
    #[cfg(feature = "experimental")]
533
    pub fn resolve_components(&self) -> crate::options::components::Bag {
6✔
534
        crate::options::components::Bag::from(&self.patterns.get().0)
6✔
535
    }
6✔
536
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc