icu_datetime/provider/calendar/
symbols.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// allowed for providers
6#![allow(clippy::exhaustive_structs, clippy::exhaustive_enums)]
7
8use crate::size_test_macro::size_test;
9use alloc::borrow::Cow;
10use icu_calendar::types::MonthCode;
11use icu_provider::prelude::*;
12use potential_utf::PotentialUtf8;
13use tinystr::{tinystr, TinyStr4};
14use zerovec::ZeroMap;
15
16size_test!(DateSymbols, date_symbols_v1_size, 3792);
17
18/// Symbol data for the months, weekdays, and eras needed to format a date.
19///
20/// For more information on date time symbols, see [`FieldSymbol`](crate::provider::fields::FieldSymbol).
21#[doc = date_symbols_v1_size!()]
22///
23/// <div class="stab unstable">
24/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
25/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
26/// to be stable, their Rust representation might not be. Use with caution.
27/// </div>
28#[derive(Debug, PartialEq, Clone, Default, yoke::Yokeable, zerofrom::ZeroFrom)]
29#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
30#[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider::calendar))]
31#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
32#[yoke(prove_covariance_manually)]
33pub struct DateSymbols<'data> {
34    /// Symbol data for months.
35    #[cfg_attr(feature = "serde", serde(borrow))]
36    pub months: months::Contexts<'data>,
37    /// Symbol data for weekdays.
38    #[cfg_attr(feature = "serde", serde(borrow))]
39    pub weekdays: weekdays::Contexts<'data>,
40    /// Symbol data for eras.
41    #[cfg_attr(feature = "serde", serde(borrow))]
42    pub eras: Eras<'data>,
43}
44
45icu_provider::data_struct!(
46    DateSymbols<'_>,
47    #[cfg(feature = "datagen")]
48);
49
50size_test!(TimeSymbols, time_symbols_v1_size, 768);
51
52/// Symbol data for the day periods needed to format a time.
53///
54/// For more information on date time symbols, see [`FieldSymbol`](crate::provider::fields::FieldSymbol).
55#[doc = time_symbols_v1_size!()]
56///
57/// <div class="stab unstable">
58/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
59/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
60/// to be stable, their Rust representation might not be. Use with caution.
61/// </div>
62#[derive(Debug, PartialEq, Clone, Default, yoke::Yokeable, zerofrom::ZeroFrom)]
63#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
64#[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider::calendar))]
65#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
66#[yoke(prove_covariance_manually)]
67pub struct TimeSymbols<'data> {
68    /// Symbol data for day periods.
69    #[cfg_attr(feature = "serde", serde(borrow))]
70    pub day_periods: day_periods::Contexts<'data>,
71}
72
73icu_provider::data_struct!(
74    TimeSymbols<'_>,
75    #[cfg(feature = "datagen")]
76);
77
78/// String data for the name, abbreviation, and narrow form of a date's era.
79///
80/// Keys of the map represent era codes, and the values are the display names.
81///
82/// Era codes are derived from CLDR data, and are calendar specific.
83/// Some examples include: `"be"`, `"0"` / `"1"`, `"bce"` / `"ce"`,
84/// `"heisei"` / `"meiji"` / `"reiwa"` / ...  Not all era codes are inherited as-is,
85/// such as for the extended Japanese calendar.
86///
87/// For more information on date time symbols, see [`FieldSymbol`](crate::provider::fields::FieldSymbol).
88///
89/// <div class="stab unstable">
90/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
91/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
92/// to be stable, their Rust representation might not be. Use with caution.
93/// </div>
94#[derive(Debug, PartialEq, Clone, Default, yoke::Yokeable, zerofrom::ZeroFrom)]
95#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
96#[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider::calendar))]
97#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
98#[yoke(prove_covariance_manually)]
99pub struct Eras<'data> {
100    /// Symbol data for era names.
101    ///
102    /// Keys are era codes, and values are display names. See [`Eras`].
103    #[cfg_attr(feature = "serde", serde(borrow))]
104    pub names: ZeroMap<'data, PotentialUtf8, str>,
105    /// Symbol data for era abbreviations.
106    ///
107    /// Keys are era codes, and values are display names. See [`Eras`].
108    #[cfg_attr(feature = "serde", serde(borrow))]
109    pub abbr: ZeroMap<'data, PotentialUtf8, str>,
110    /// Symbol data for era narrow forms.
111    ///
112    /// Keys are era codes, and values are display names. See [`Eras`].
113    #[cfg_attr(feature = "serde", serde(borrow))]
114    pub narrow: ZeroMap<'data, PotentialUtf8, str>,
115}
116
117// Note: the SymbolsV* struct doc strings metadata are attached to `$name` in the macro invocation to
118// avoid macro parsing ambiguity caused by other metadata already attached to `$symbols`.
119macro_rules! symbols {
120    ($(#[$symbols_attr:meta])*  $name: ident, $field_id: ident, $symbols: item) => {
121
122        $(#[$symbols_attr])*
123        #[doc = concat!("Formatting symbols for [`",
124                stringify!($field_id),
125                "`](crate::provider::fields::FieldSymbol::",
126                stringify!($field_id),
127                ").\n\n",
128                "For more information on date time symbols, see [`FieldSymbol`](crate::provider::fields::FieldSymbol).")]
129        pub mod $name {
130            use super::*;
131
132            #[derive(Debug, PartialEq, Clone, zerofrom::ZeroFrom, yoke::Yokeable)]
133            #[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
134            #[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider::calendar::$name))]
135            #[cfg_attr(feature = "serde", derive(serde::Deserialize))]
136            #[yoke(prove_covariance_manually)]
137            #[doc = concat!("Locale data for ", stringify!($field_id), " corresponding to the symbols.")]
138            ///
139            /// <div class="stab unstable">
140            /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
141            /// including in SemVer minor releases. While the serde representation of data structs is guaranteed
142            /// to be stable, their Rust representation might not be. Use with caution.
143            /// </div>
144            $symbols
145
146            // UTS 35 specifies that `format` widths are mandatory,
147            // except for `short`.
148            #[derive(Debug, PartialEq, Clone, Default, yoke::Yokeable, zerofrom::ZeroFrom)]
149            #[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
150            #[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider::calendar::$name))]
151            #[cfg_attr(feature = "serde", derive(serde::Deserialize))]
152            #[yoke(prove_covariance_manually)]
153            #[doc = concat!("Symbol data for the \"format\" style formatting of ", stringify!($field_id),
154                ".\n\nThe format style is used in contexts where it is different from the stand-alone form, ex: ",
155                "a case inflected form where the stand-alone form is the nominative case.")]
156            ///
157            /// <div class="stab unstable">
158            /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
159            /// including in SemVer minor releases. While the serde representation of data structs is guaranteed
160            /// to be stable, their Rust representation might not be. Use with caution.
161            /// </div>
162            pub struct FormatWidths<'data> {
163                #[doc = concat!("Abbreviated length symbol for \"format\" style symbol for ", stringify!($name), ".")]
164                #[cfg_attr(feature = "serde", serde(borrow))]
165                pub abbreviated: Symbols<'data>,
166                #[doc = concat!("Narrow length symbol for \"format\" style symbol for ", stringify!($name), ".")]
167                #[cfg_attr(feature = "serde", serde(borrow))]
168                pub narrow: Symbols<'data>,
169                #[doc = concat!("Short length symbol for \"format\" style symbol for ", stringify!($name), ", if present.")]
170                #[cfg_attr(feature = "serde", serde(borrow))]
171                pub short: Option<Symbols<'data>>,
172                #[doc = concat!("Wide length symbol for \"format\" style symbol for ", stringify!($name), ".")]
173                #[cfg_attr(feature = "serde", serde(borrow))]
174                pub wide: Symbols<'data>,
175            }
176
177            // UTS 35 specifies that `stand_alone` widths are optional
178            #[derive(Debug, PartialEq, Clone, Default, yoke::Yokeable, zerofrom::ZeroFrom)]
179            #[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
180            #[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider::calendar::$name))]
181            #[cfg_attr(feature = "serde", derive(serde::Deserialize))]
182            #[yoke(prove_covariance_manually)]
183            #[doc = concat!("Symbol data for the \"stand-alone\" style formatting of ", stringify!($field_id),
184                ".\n\nThe stand-alone style is used in contexts where the field is displayed by itself.")]
185            ///
186            /// <div class="stab unstable">
187            /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
188            /// including in SemVer minor releases. While the serde representation of data structs is guaranteed
189            /// to be stable, their Rust representation might not be. Use with caution.
190            /// </div>
191            pub struct StandAloneWidths<'data> {
192                #[doc = concat!("Abbreviated length symbol for \"stand-alone\" style symbol for ", stringify!($name), ".")]
193                #[cfg_attr(feature = "serde", serde(borrow))]
194                pub abbreviated: Option<Symbols<'data>>,
195                #[doc = concat!("Narrow length symbol for \"stand-alone\" style symbol for ", stringify!($name), ".")]
196                #[cfg_attr(feature = "serde", serde(borrow))]
197                pub narrow: Option<Symbols<'data>>,
198                #[doc = concat!("Short length symbol for \"stand-alone\" style symbol for ", stringify!($name), ".")]
199                #[cfg_attr(feature = "serde", serde(borrow))]
200                pub short: Option<Symbols<'data>>,
201                #[doc = concat!("Wide length symbol for \"stand-alone\" style symbol for ", stringify!($name), ".")]
202                #[cfg_attr(feature = "serde", serde(borrow))]
203                pub wide: Option<Symbols<'data>>,
204            }
205
206            #[derive(Debug, PartialEq, Clone, Default, yoke::Yokeable, zerofrom::ZeroFrom)]
207            #[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
208            #[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider::calendar::$name))]
209            #[cfg_attr(feature = "serde", derive(serde::Deserialize))]
210            #[yoke(prove_covariance_manually)]
211            #[doc = concat!("The struct containing the symbol data for ", stringify!($field_id),
212                " that contains the \"format\" style symbol data ([`FormatWidths`]) and \"stand-alone\" style symbol data ([`StandAloneWidths`]).")]
213            ///
214            /// <div class="stab unstable">
215            /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
216            /// including in SemVer minor releases. While the serde representation of data structs is guaranteed
217            /// to be stable, their Rust representation might not be. Use with caution.
218            /// </div>
219            pub struct Contexts<'data> {
220                /// The symbol data for "format" style symbols.
221                #[cfg_attr(feature = "serde", serde(borrow))]
222                pub format: FormatWidths<'data>,
223                /// The symbol data for "stand-alone" style symbols.
224                #[cfg_attr(feature = "serde", serde(borrow))]
225                pub stand_alone: Option<StandAloneWidths<'data>>,
226            }
227        }
228    };
229}
230
231symbols!(
232    months,
233    Month,
234    #[allow(clippy::large_enum_variant)]
235    pub enum Symbols<'data> {
236        /// Twelve symbols for a solar calendar
237        ///
238        /// This is an optimization to reduce data size.
239        SolarTwelve(
240            #[cfg_attr(
241                feature = "serde",
242                serde(
243                    borrow,
244                    deserialize_with = "icu_provider::serde_borrow_de_utils::array_of_cow"
245                )
246            )]
247            [Cow<'data, str>; 12],
248        ),
249        /// A calendar with an arbitrary number of months, potentially including leap months
250        #[cfg_attr(feature = "serde", serde(borrow))]
251        Other(ZeroMap<'data, MonthCode, str>),
252    }
253);
254
255impl months::Symbols<'_> {
256    /// Get the symbol for the given month code
257    pub fn get(&self, code: MonthCode) -> Option<&str> {
258        match *self {
259            Self::SolarTwelve(ref arr) => {
260                // The tinystr macro doesn't work in match patterns
261                // so we use consts first
262                const CODE_1: TinyStr4 = tinystr!(4, "M01");
263                const CODE_2: TinyStr4 = tinystr!(4, "M02");
264                const CODE_3: TinyStr4 = tinystr!(4, "M03");
265                const CODE_4: TinyStr4 = tinystr!(4, "M04");
266                const CODE_5: TinyStr4 = tinystr!(4, "M05");
267                const CODE_6: TinyStr4 = tinystr!(4, "M06");
268                const CODE_7: TinyStr4 = tinystr!(4, "M07");
269                const CODE_8: TinyStr4 = tinystr!(4, "M08");
270                const CODE_9: TinyStr4 = tinystr!(4, "M09");
271                const CODE_10: TinyStr4 = tinystr!(4, "M10");
272                const CODE_11: TinyStr4 = tinystr!(4, "M11");
273                const CODE_12: TinyStr4 = tinystr!(4, "M12");
274                let idx = match code.0 {
275                    CODE_1 => 0,
276                    CODE_2 => 1,
277                    CODE_3 => 2,
278                    CODE_4 => 3,
279                    CODE_5 => 4,
280                    CODE_6 => 5,
281                    CODE_7 => 6,
282                    CODE_8 => 7,
283                    CODE_9 => 8,
284                    CODE_10 => 9,
285                    CODE_11 => 10,
286                    CODE_12 => 11,
287                    _ => return None,
288                };
289                arr.get(idx).map(|x| &**x)
290            }
291            Self::Other(ref map) => map.get(&code),
292        }
293    }
294}
295
296impl Default for months::Symbols<'_> {
297    fn default() -> Self {
298        Self::Other(Default::default())
299    }
300}
301
302symbols!(
303    weekdays,
304    Weekday,
305    #[derive(Default)]
306    pub struct Symbols<'data>(
307        #[cfg_attr(
308            feature = "serde",
309            serde(
310                borrow,
311                deserialize_with = "icu_provider::serde_borrow_de_utils::array_of_cow"
312            )
313        )]
314        pub [Cow<'data, str>; 7],
315    );
316);
317
318symbols!(
319    day_periods,
320    DayPeriod,
321    #[derive(Default)]
322    pub struct Symbols<'data> {
323        /// Day period for AM (before noon).
324        #[cfg_attr(feature = "serde", serde(borrow))]
325        pub am: Cow<'data, str>,
326        /// Day period for PM (after noon).
327        #[cfg_attr(feature = "serde", serde(borrow))]
328        pub pm: Cow<'data, str>,
329        #[cfg_attr(
330            feature = "serde",
331            serde(
332                borrow,
333                deserialize_with = "icu_provider::serde_borrow_de_utils::option_of_cow"
334            )
335        )]
336        /// Day period for noon, in locales that support it.
337        pub noon: Option<Cow<'data, str>>,
338        #[cfg_attr(
339            feature = "serde",
340            serde(
341                borrow,
342                deserialize_with = "icu_provider::serde_borrow_de_utils::option_of_cow"
343            )
344        )]
345        /// Day period for midnight, in locales that support it.
346        pub midnight: Option<Cow<'data, str>>,
347    }
348);
349
350#[cfg(all(test, feature = "datagen"))]
351mod test {
352    use super::*;
353    use tinystr::tinystr;
354
355    fn serialize_date() -> Vec<u8> {
356        let months = [
357            (&MonthCode(tinystr!(4, "M01")), "January"),
358            (&MonthCode(tinystr!(4, "M02")), "February"),
359            (&MonthCode(tinystr!(4, "M03")), "March"),
360            (&MonthCode(tinystr!(4, "M04")), "April"),
361            (&MonthCode(tinystr!(4, "M05")), "May"),
362            (&MonthCode(tinystr!(4, "M06")), "June"),
363            (&MonthCode(tinystr!(4, "M07")), "July"),
364            (&MonthCode(tinystr!(4, "M08")), "August"),
365            (&MonthCode(tinystr!(4, "M09")), "September"),
366            (&MonthCode(tinystr!(4, "M10")), "October"),
367            (&MonthCode(tinystr!(4, "M11")), "November"),
368            (&MonthCode(tinystr!(4, "M12")), "December"),
369        ];
370        let months = months::Symbols::Other(months.iter().copied().collect());
371
372        let weekdays = weekdays::Symbols([
373            Cow::Borrowed("Monday"),
374            Cow::Borrowed("Tuesday"),
375            Cow::Borrowed("Wednesday"),
376            Cow::Borrowed("Thursday"),
377            Cow::Borrowed("Friday"),
378            Cow::Borrowed("Saturday"),
379            Cow::Borrowed("Sunday"),
380        ]);
381
382        bincode::serialize(&DateSymbols {
383            months: months::Contexts {
384                format: months::FormatWidths {
385                    abbreviated: months.clone(),
386                    narrow: months.clone(),
387                    short: Some(months.clone()),
388                    wide: months.clone(),
389                },
390                stand_alone: Some(months::StandAloneWidths {
391                    abbreviated: Some(months.clone()),
392                    narrow: Some(months.clone()),
393                    short: Some(months.clone()),
394                    wide: Some(months.clone()),
395                }),
396            },
397            weekdays: weekdays::Contexts {
398                format: weekdays::FormatWidths {
399                    abbreviated: weekdays.clone(),
400                    narrow: weekdays.clone(),
401                    short: Some(weekdays.clone()),
402                    wide: weekdays.clone(),
403                },
404                stand_alone: Some(weekdays::StandAloneWidths {
405                    abbreviated: Some(weekdays.clone()),
406                    narrow: Some(weekdays.clone()),
407                    short: Some(weekdays.clone()),
408                    wide: Some(weekdays.clone()),
409                }),
410            },
411            eras: Eras {
412                names: ZeroMap::new(),
413                abbr: ZeroMap::new(),
414                narrow: ZeroMap::new(),
415            },
416        })
417        .unwrap()
418    }
419
420    fn serialize_time() -> Vec<u8> {
421        let day_periods = day_periods::Symbols {
422            am: Cow::Borrowed("am"),
423            pm: Cow::Borrowed("pm"),
424            noon: Some(Cow::Borrowed("noon")),
425            midnight: None,
426        };
427
428        bincode::serialize(&TimeSymbols {
429            day_periods: day_periods::Contexts {
430                format: day_periods::FormatWidths {
431                    abbreviated: day_periods.clone(),
432                    narrow: day_periods.clone(),
433                    short: Some(day_periods.clone()),
434                    wide: day_periods.clone(),
435                },
436                stand_alone: Some(day_periods::StandAloneWidths {
437                    abbreviated: Some(day_periods.clone()),
438                    narrow: Some(day_periods.clone()),
439                    short: Some(day_periods.clone()),
440                    wide: Some(day_periods.clone()),
441                }),
442            },
443        })
444        .unwrap()
445    }
446
447    #[test]
448    fn weekdays_borrows() {
449        let bytes = serialize_date();
450        let de = bincode::deserialize::<DateSymbols>(&bytes).unwrap();
451
452        assert!(matches!(de.weekdays.format.narrow.0[2], Cow::Borrowed(_)));
453        assert!(matches!(
454            de.weekdays.format.short.as_ref().unwrap().0[4],
455            Cow::Borrowed(_)
456        ));
457    }
458
459    #[test]
460    fn day_periods_borrows() {
461        let bytes = serialize_time();
462        let de = bincode::deserialize::<TimeSymbols>(&bytes).unwrap();
463
464        assert!(matches!(
465            de.day_periods.format.narrow.noon,
466            Some(Cow::Borrowed(_))
467        ));
468        assert!(matches!(
469            de.day_periods.format.short.as_ref().unwrap().noon,
470            Some(Cow::Borrowed(_))
471        ));
472
473        assert!(matches!(de.day_periods.format.narrow.am, Cow::Borrowed(_)));
474        assert!(matches!(
475            de.day_periods.format.short.as_ref().unwrap().am,
476            Cow::Borrowed(_)
477        ));
478    }
479}