icu_calendar/
provider.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//! 🚧 \[Unstable\] Data provider struct definitions for this ICU4X component.
6//!
7//! <div class="stab unstable">
8//! 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
9//! including in SemVer minor releases. While the serde representation of data structs is guaranteed
10//! to be stable, their Rust representation might not be. Use with caution.
11//! </div>
12//!
13//! Read more about data providers: [`icu_provider`]
14
15// Provider structs must be stable
16#![allow(clippy::exhaustive_structs, clippy::exhaustive_enums)]
17
18use crate::types::Weekday;
19use icu_provider::fallback::{LocaleFallbackConfig, LocaleFallbackPriority};
20use icu_provider::prelude::*;
21use tinystr::TinyStr16;
22use zerovec::ZeroVec;
23
24#[cfg(feature = "compiled_data")]
25#[derive(Debug)]
26/// Baked data
27///
28/// <div class="stab unstable">
29/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
30/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
31/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
32/// </div>
33pub struct Baked;
34
35#[cfg(feature = "compiled_data")]
36#[allow(unused_imports)]
37const _: () = {
38    use icu_calendar_data::*;
39    pub mod icu {
40        pub use crate as calendar;
41        pub use icu_locale as locale;
42    }
43    make_provider!(Baked);
44    impl_calendar_japanese_modern_v1!(Baked);
45    impl_calendar_japanese_extended_v1!(Baked);
46    impl_calendar_week_v1!(Baked);
47};
48
49icu_provider::data_marker!(
50    /// Modern Japanese era names
51    CalendarJapaneseModernV1,
52    "calendar/japanese/modern/v1",
53    JapaneseEras<'static>,
54    is_singleton = true
55);
56icu_provider::data_marker!(
57    /// Full Japanese era names
58    CalendarJapaneseExtendedV1,
59    "calendar/japanese/extended/v1",
60    JapaneseEras<'static>,
61    is_singleton = true
62);
63icu_provider::data_marker!(
64    /// Week information
65    CalendarWeekV1,
66    "calendar/week/v1",
67    WeekData,
68    fallback_config = {
69        let mut config = LocaleFallbackConfig::default();
70        config.priority = LocaleFallbackPriority::Region;
71        config
72    },
73);
74
75#[cfg(feature = "datagen")]
76/// The latest minimum set of markers required by this component.
77pub const MARKERS: &[DataMarkerInfo] = &[
78    CalendarJapaneseModernV1::INFO,
79    CalendarJapaneseExtendedV1::INFO,
80    CalendarWeekV1::INFO,
81];
82
83/// The date at which an era started
84///
85/// The order of fields in this struct is important!
86///
87/// <div class="stab unstable">
88/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
89/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
90/// to be stable, their Rust representation might not be. Use with caution.
91/// </div>
92#[zerovec::make_ule(EraStartDateULE)]
93#[derive(
94    Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug, yoke::Yokeable, zerofrom::ZeroFrom,
95)]
96#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
97#[cfg_attr(feature = "datagen", databake(path = icu_calendar::provider))]
98#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
99#[cfg_attr(not(feature = "alloc"), zerovec::skip_derive(ZeroMapKV))]
100pub struct EraStartDate {
101    /// The year the era started in
102    pub year: i32,
103    /// The month the era started in
104    pub month: u8,
105    /// The day the era started in
106    pub day: u8,
107}
108
109/// A data structure containing the necessary era data for constructing a
110/// [`Japanese`](crate::cal::Japanese) calendar object
111///
112/// <div class="stab unstable">
113/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
114/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
115/// to be stable, their Rust representation might not be. Use with caution.
116/// </div>
117#[derive(Debug, PartialEq, Clone, Default, yoke::Yokeable, zerofrom::ZeroFrom)]
118#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
119#[cfg_attr(feature = "datagen", databake(path = icu_calendar::provider))]
120#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
121pub struct JapaneseEras<'data> {
122    /// A map from era start dates to their era codes
123    #[cfg_attr(feature = "serde", serde(borrow))]
124    pub dates_to_eras: ZeroVec<'data, (EraStartDate, TinyStr16)>,
125}
126
127icu_provider::data_struct!(
128    JapaneseEras<'_>,
129    #[cfg(feature = "datagen")]
130);
131
132/// An ICU4X mapping to a subset of CLDR weekData.
133/// See CLDR-JSON's weekData.json for more context.
134///
135/// <div class="stab unstable">
136/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
137/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
138/// to be stable, their Rust representation might not be. Use with caution.
139/// </div>
140#[derive(Clone, Copy, Debug, PartialEq, yoke::Yokeable, zerofrom::ZeroFrom)]
141#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
142#[cfg_attr(feature = "datagen", databake(path = icu_calendar::provider))]
143#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
144#[allow(clippy::exhaustive_structs)] // used in data provider
145pub struct WeekData {
146    /// The first day of a week.
147    pub first_weekday: Weekday,
148    /// Bitset representing weekdays that are part of the 'weekend', for calendar purposes.
149    /// The number of days can be different between locales, and may not be contiguous.
150    pub weekend: WeekdaySet,
151}
152
153icu_provider::data_struct!(
154    WeekData,
155    #[cfg(feature = "datagen")]
156);
157
158/// Bitset representing weekdays.
159//
160// This Bitset uses an [u8] to represent the weekend, thus leaving one bit free.
161// Each bit represents a day in the following order:
162//
163//   β”Œβ–·Mon
164//   β”‚β”Œβ–·Tue
165//   β”‚β”‚β”Œβ–·Wed
166//   β”‚β”‚β”‚β”Œβ–·Thu
167//   β”‚β”‚β”‚β”‚ β”Œβ–·Fri
168//   β”‚β”‚β”‚β”‚ β”‚β”Œβ–·Sat
169//   β”‚β”‚β”‚β”‚ β”‚β”‚β”Œβ–·Sun
170//   β”‚β”‚β”‚β”‚ β”‚β”‚β”‚
171// 0b0000_1010
172//
173// Please note that this is not a range, this are the discrete days representing a weekend. Other examples:
174// 0b0101_1000 -> Tue, Thu, Fri
175// 0b0000_0110 -> Sat, Sun
176#[derive(Clone, Copy, Debug, PartialEq)]
177pub struct WeekdaySet(u8);
178
179impl WeekdaySet {
180    /// Returns whether the set contains the day.
181    pub const fn contains(&self, day: Weekday) -> bool {
182        self.0 & day.bit_value() != 0
183    }
184}
185
186impl WeekdaySet {
187    /// Creates a new [WeekdaySet] using the provided days.
188    pub const fn new(days: &[Weekday]) -> Self {
189        let mut i = 0;
190        let mut w = 0;
191        #[expect(clippy::indexing_slicing)]
192        while i < days.len() {
193            w |= days[i].bit_value();
194            i += 1;
195        }
196        Self(w)
197    }
198}
199
200impl Weekday {
201    /// Defines the bit order used for encoding and reading weekend days.
202    const fn bit_value(self) -> u8 {
203        match self {
204            Weekday::Monday => 1 << 6,
205            Weekday::Tuesday => 1 << 5,
206            Weekday::Wednesday => 1 << 4,
207            Weekday::Thursday => 1 << 3,
208            Weekday::Friday => 1 << 2,
209            Weekday::Saturday => 1 << 1,
210            Weekday::Sunday => 1 << 0,
211        }
212    }
213}
214
215#[cfg(feature = "datagen")]
216impl databake::Bake for WeekdaySet {
217    fn bake(&self, ctx: &databake::CrateEnv) -> databake::TokenStream {
218        ctx.insert("icu_calendar");
219        let days =
220            crate::week::WeekdaySetIterator::new(Weekday::Monday, *self).map(|d| d.bake(ctx));
221        databake::quote! {
222            icu_calendar::provider::WeekdaySet::new(&[#(#days),*])
223        }
224    }
225}
226
227#[cfg(feature = "datagen")]
228impl databake::BakeSize for WeekdaySet {
229    fn borrows_size(&self) -> usize {
230        0
231    }
232}
233
234#[cfg(feature = "datagen")]
235impl serde::Serialize for WeekdaySet {
236    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
237    where
238        S: serde::Serializer,
239    {
240        if serializer.is_human_readable() {
241            use serde::ser::SerializeSeq;
242
243            let mut seq = serializer.serialize_seq(None)?;
244            for day in crate::week::WeekdaySetIterator::new(Weekday::Monday, *self) {
245                seq.serialize_element(&day)?;
246            }
247            seq.end()
248        } else {
249            self.0.serialize(serializer)
250        }
251    }
252}
253
254#[cfg(feature = "serde")]
255impl<'de> serde::Deserialize<'de> for WeekdaySet {
256    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
257    where
258        D: serde::Deserializer<'de>,
259    {
260        if deserializer.is_human_readable() {
261            use core::marker::PhantomData;
262
263            struct Visitor<'de>(PhantomData<&'de ()>);
264            impl<'de> serde::de::Visitor<'de> for Visitor<'de> {
265                type Value = WeekdaySet;
266                fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
267                    core::write!(f, "a sequence of Weekdays")
268                }
269                fn visit_seq<A: serde::de::SeqAccess<'de>>(
270                    self,
271                    mut seq: A,
272                ) -> Result<Self::Value, A::Error> {
273                    let mut set = WeekdaySet::new(&[]);
274                    while let Some(day) = seq.next_element::<Weekday>()? {
275                        set.0 |= day.bit_value();
276                    }
277                    Ok(set)
278                }
279            }
280            deserializer.deserialize_seq(Visitor(PhantomData))
281        } else {
282            u8::deserialize(deserializer).map(Self)
283        }
284    }
285}
286
287#[test]
288#[cfg(feature = "datagen")]
289fn test_weekdayset_bake() {
290    databake::test_bake!(
291        WeekdaySet,
292        const,
293        crate::provider::WeekdaySet::new(&[
294            crate::types::Weekday::Monday,
295            crate::types::Weekday::Wednesday,
296            crate::types::Weekday::Friday
297        ]),
298        icu_calendar
299    );
300}
301
302#[test]
303fn test_weekdayset_new() {
304    use Weekday::*;
305
306    let sat_sun_bitmap = Saturday.bit_value() | Sunday.bit_value();
307    let sat_sun_weekend = WeekdaySet::new(&[Saturday, Sunday]);
308    assert_eq!(sat_sun_bitmap, sat_sun_weekend.0);
309
310    let fri_sat_bitmap = Friday.bit_value() | Saturday.bit_value();
311    let fri_sat_weekend = WeekdaySet::new(&[Friday, Saturday]);
312    assert_eq!(fri_sat_bitmap, fri_sat_weekend.0);
313
314    let fri_sun_bitmap = Friday.bit_value() | Sunday.bit_value();
315    let fri_sun_weekend = WeekdaySet::new(&[Friday, Sunday]);
316    assert_eq!(fri_sun_bitmap, fri_sun_weekend.0);
317
318    let fri_bitmap = Friday.bit_value();
319    let fri_weekend = WeekdaySet::new(&[Friday, Friday]);
320    assert_eq!(fri_bitmap, fri_weekend.0);
321
322    let sun_mon_bitmap = Sunday.bit_value() | Monday.bit_value();
323    let sun_mon_weekend = WeekdaySet::new(&[Sunday, Monday]);
324    assert_eq!(sun_mon_bitmap, sun_mon_weekend.0);
325
326    let mon_sun_bitmap = Monday.bit_value() | Sunday.bit_value();
327    let mon_sun_weekend = WeekdaySet::new(&[Monday, Sunday]);
328    assert_eq!(mon_sun_bitmap, mon_sun_weekend.0);
329
330    let mon_bitmap = Monday.bit_value();
331    let mon_weekend = WeekdaySet::new(&[Monday]);
332    assert_eq!(mon_bitmap, mon_weekend.0);
333}