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
18pub mod chinese_based;
19pub mod islamic;
20pub use chinese_based::{ChineseCacheV1Marker, DangiCacheV1Marker};
21pub use islamic::{IslamicObservationalCacheV1Marker, IslamicUmmAlQuraCacheV1Marker};
22
23use crate::types::IsoWeekday;
24use core::str::FromStr;
25use icu_provider::prelude::*;
26use tinystr::TinyStr16;
27use zerovec::ZeroVec;
28
29#[cfg(feature = "compiled_data")]
30#[derive(Debug)]
31/// Baked data
32///
33/// <div class="stab unstable">
34/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
35/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
36/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
37/// </div>
38pub struct Baked;
39
40#[cfg(feature = "compiled_data")]
41const _: () = {
42    pub mod icu {
43        pub use crate as calendar;
44        #[allow(unused_imports)] // baked data may or may not need this
45        pub use icu_locid_transform as locid_transform;
46    }
47    icu_calendar_data::make_provider!(Baked);
48    icu_calendar_data::impl_calendar_chinesecache_v1!(Baked);
49    icu_calendar_data::impl_calendar_dangicache_v1!(Baked);
50    icu_calendar_data::impl_calendar_islamicobservationalcache_v1!(Baked);
51    icu_calendar_data::impl_calendar_islamicummalquracache_v1!(Baked);
52    icu_calendar_data::impl_calendar_japanese_v1!(Baked);
53    icu_calendar_data::impl_calendar_japanext_v1!(Baked);
54    icu_calendar_data::impl_datetime_week_data_v1!(Baked);
55    icu_calendar_data::impl_datetime_week_data_v2!(Baked);
56};
57
58#[cfg(feature = "datagen")]
59/// The latest minimum set of keys required by this component.
60pub const KEYS: &[DataKey] = &[
61    ChineseCacheV1Marker::KEY,
62    DangiCacheV1Marker::KEY,
63    IslamicObservationalCacheV1Marker::KEY,
64    IslamicUmmAlQuraCacheV1Marker::KEY,
65    JapaneseErasV1Marker::KEY,
66    JapaneseExtendedErasV1Marker::KEY,
67    WeekDataV2Marker::KEY,
68    // We include the duplicate data for now, as icu_datetime loads it directly
69    // https://github.com/unicode-org/icu4x/pull/4364#discussion_r1419877997
70    WeekDataV1Marker::KEY,
71];
72
73/// The date at which an era started
74///
75/// The order of fields in this struct is important!
76///
77/// <div class="stab unstable">
78/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
79/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
80/// to be stable, their Rust representation might not be. Use with caution.
81/// </div>
82#[zerovec::make_ule(EraStartDateULE)]
83#[derive(
84    Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug, yoke::Yokeable, zerofrom::ZeroFrom,
85)]
86#[cfg_attr(
87    feature = "datagen",
88    derive(serde::Serialize, databake::Bake),
89    databake(path = icu_calendar::provider),
90)]
91#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
92pub struct EraStartDate {
93    /// The year the era started in
94    pub year: i32,
95    /// The month the era started in
96    pub month: u8,
97    /// The day the era started in
98    pub day: u8,
99}
100
101/// A data structure containing the necessary era data for constructing a
102/// [`Japanese`](crate::japanese::Japanese) calendar object
103///
104/// <div class="stab unstable">
105/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
106/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
107/// to be stable, their Rust representation might not be. Use with caution.
108/// </div>
109#[icu_provider::data_struct(
110    marker(JapaneseErasV1Marker, "calendar/japanese@1", singleton),
111    marker(JapaneseExtendedErasV1Marker, "calendar/japanext@1", singleton)
112)]
113#[derive(Debug, PartialEq, Clone, Default)]
114#[cfg_attr(
115    feature = "datagen",
116    derive(serde::Serialize, databake::Bake),
117    databake(path = icu_calendar::provider),
118)]
119#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
120pub struct JapaneseErasV1<'data> {
121    /// A map from era start dates to their era codes
122    #[cfg_attr(feature = "serde", serde(borrow))]
123    pub dates_to_eras: ZeroVec<'data, (EraStartDate, TinyStr16)>,
124}
125
126impl FromStr for EraStartDate {
127    type Err = ();
128    fn from_str(mut s: &str) -> Result<Self, Self::Err> {
129        let sign = if let Some(suffix) = s.strip_prefix('-') {
130            s = suffix;
131            -1
132        } else {
133            1
134        };
135
136        let mut split = s.split('-');
137        let year = split.next().ok_or(())?.parse::<i32>().map_err(|_| ())? * sign;
138        let month = split.next().ok_or(())?.parse().map_err(|_| ())?;
139        let day = split.next().ok_or(())?.parse().map_err(|_| ())?;
140
141        Ok(EraStartDate { year, month, day })
142    }
143}
144
145/// An ICU4X mapping to a subset of CLDR weekData.
146/// See CLDR-JSON's weekData.json for more context.
147///
148/// <div class="stab unstable">
149/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
150/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
151/// to be stable, their Rust representation might not be. Use with caution.
152/// </div>
153#[icu_provider::data_struct(marker(
154    WeekDataV1Marker,
155    "datetime/week_data@1",
156    fallback_by = "region"
157))]
158#[derive(Clone, Copy, Debug, PartialEq)]
159#[cfg_attr(
160    feature = "datagen",
161    derive(serde::Serialize, databake::Bake),
162    databake(path = icu_calendar::provider),
163)]
164#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
165#[allow(clippy::exhaustive_structs)] // used in data provider
166pub struct WeekDataV1 {
167    /// The first day of a week.
168    pub first_weekday: IsoWeekday,
169    /// For a given week, the minimum number of that week's days present in a given month or year for the week to be considered part of that month or year.
170    pub min_week_days: u8,
171}
172
173/// An ICU4X mapping to a subset of CLDR weekData.
174/// See CLDR-JSON's weekData.json for more context.
175///
176/// <div class="stab unstable">
177/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
178/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
179/// to be stable, their Rust representation might not be. Use with caution.
180/// </div>
181#[icu_provider::data_struct(marker(
182    WeekDataV2Marker,
183    "datetime/week_data@2",
184    fallback_by = "region"
185))]
186#[derive(Clone, Copy, Debug, PartialEq)]
187#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake), databake(path = icu_calendar::provider))]
188#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
189#[allow(clippy::exhaustive_structs)] // used in data provider
190pub struct WeekDataV2 {
191    /// The first day of a week.
192    pub first_weekday: IsoWeekday,
193    /// For a given week, the minimum number of that week's days present in a given month or year for the week to be considered part of that month or year.
194    pub min_week_days: u8,
195    /// Bitset representing weekdays that are part of the 'weekend', for calendar purposes.
196    /// The number of days can be different between locales, and may not be contiguous.
197    pub weekend: WeekdaySet,
198}
199
200/// Bitset representing weekdays.
201//
202// This Bitset uses an [u8] to represent the weekend, thus leaving one bit free.
203// Each bit represents a day in the following order:
204//
205//   β”Œβ–·Mon
206//   β”‚β”Œβ–·Tue
207//   β”‚β”‚β”Œβ–·Wed
208//   β”‚β”‚β”‚β”Œβ–·Thu
209//   β”‚β”‚β”‚β”‚ β”Œβ–·Fri
210//   β”‚β”‚β”‚β”‚ β”‚β”Œβ–·Sat
211//   β”‚β”‚β”‚β”‚ β”‚β”‚β”Œβ–·Sun
212//   β”‚β”‚β”‚β”‚ β”‚β”‚β”‚
213// 0b0000_1010
214//
215// Please note that this is not a range, this are the discrete days representing a weekend. Other examples:
216// 0b0101_1000 -> Tue, Thu, Fri
217// 0b0000_0110 -> Sat, Sun
218#[derive(Clone, Copy, Debug, PartialEq)]
219pub struct WeekdaySet(u8);
220
221impl WeekdaySet {
222    /// Returns whether the set contains the day.
223    pub const fn contains(&self, day: IsoWeekday) -> bool {
224        self.0 & day.bit_value() != 0
225    }
226}
227
228impl WeekdaySet {
229    /// Creates a new [WeekdaySet] using the provided days.
230    pub const fn new(days: &[IsoWeekday]) -> Self {
231        let mut i = 0;
232        let mut w = 0;
233        #[allow(clippy::indexing_slicing)]
234        while i < days.len() {
235            w |= days[i].bit_value();
236            i += 1;
237        }
238        Self(w)
239    }
240}
241
242impl IsoWeekday {
243    /// Defines the bit order used for encoding and reading weekend days.
244    const fn bit_value(&self) -> u8 {
245        match self {
246            IsoWeekday::Monday => 1 << 6,
247            IsoWeekday::Tuesday => 1 << 5,
248            IsoWeekday::Wednesday => 1 << 4,
249            IsoWeekday::Thursday => 1 << 3,
250            IsoWeekday::Friday => 1 << 2,
251            IsoWeekday::Saturday => 1 << 1,
252            IsoWeekday::Sunday => 1 << 0,
253        }
254    }
255}
256
257#[cfg(feature = "datagen")]
258impl databake::Bake for WeekdaySet {
259    fn bake(&self, ctx: &databake::CrateEnv) -> databake::TokenStream {
260        ctx.insert("icu_calendar");
261        let days =
262            crate::week_of::WeekdaySetIterator::new(IsoWeekday::Monday, *self).map(|d| d.bake(ctx));
263        databake::quote! {
264            icu_calendar::provider::WeekdaySet::new(&[#(#days),*])
265        }
266    }
267}
268
269#[cfg(feature = "datagen")]
270impl serde::Serialize for WeekdaySet {
271    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
272    where
273        S: serde::Serializer,
274    {
275        if serializer.is_human_readable() {
276            crate::week_of::WeekdaySetIterator::new(IsoWeekday::Monday, *self)
277                .collect::<alloc::vec::Vec<_>>()
278                .serialize(serializer)
279        } else {
280            self.0.serialize(serializer)
281        }
282    }
283}
284
285#[cfg(feature = "serde")]
286impl<'de> serde::Deserialize<'de> for WeekdaySet {
287    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
288    where
289        D: serde::Deserializer<'de>,
290    {
291        if deserializer.is_human_readable() {
292            alloc::vec::Vec::<IsoWeekday>::deserialize(deserializer).map(|s| Self::new(&s))
293        } else {
294            u8::deserialize(deserializer).map(Self)
295        }
296    }
297}
298
299#[test]
300fn test_weekdayset_bake() {
301    databake::test_bake!(
302        WeekdaySet,
303        const: crate::provider::WeekdaySet::new(
304            &[crate::types::IsoWeekday::Monday, crate::types::IsoWeekday::Wednesday, crate::types::IsoWeekday::Friday]),
305        icu_calendar
306    );
307}
308
309#[test]
310fn test_weekdayset_new() {
311    use IsoWeekday::*;
312
313    let sat_sun_bitmap = Saturday.bit_value() | Sunday.bit_value();
314    let sat_sun_weekend = WeekdaySet::new(&[Saturday, Sunday]);
315    assert_eq!(sat_sun_bitmap, sat_sun_weekend.0);
316
317    let fri_sat_bitmap = Friday.bit_value() | Saturday.bit_value();
318    let fri_sat_weekend = WeekdaySet::new(&[Friday, Saturday]);
319    assert_eq!(fri_sat_bitmap, fri_sat_weekend.0);
320
321    let fri_sun_bitmap = Friday.bit_value() | Sunday.bit_value();
322    let fri_sun_weekend = WeekdaySet::new(&[Friday, Sunday]);
323    assert_eq!(fri_sun_bitmap, fri_sun_weekend.0);
324
325    let fri_bitmap = Friday.bit_value();
326    let fri_weekend = WeekdaySet::new(&[Friday, Friday]);
327    assert_eq!(fri_bitmap, fri_weekend.0);
328
329    let sun_mon_bitmap = Sunday.bit_value() | Monday.bit_value();
330    let sun_mon_weekend = WeekdaySet::new(&[Sunday, Monday]);
331    assert_eq!(sun_mon_bitmap, sun_mon_weekend.0);
332
333    let mon_sun_bitmap = Monday.bit_value() | Sunday.bit_value();
334    let mon_sun_weekend = WeekdaySet::new(&[Monday, Sunday]);
335    assert_eq!(mon_sun_bitmap, mon_sun_weekend.0);
336
337    let mon_bitmap = Monday.bit_value();
338    let mon_weekend = WeekdaySet::new(&[Monday]);
339    assert_eq!(mon_bitmap, mon_weekend.0);
340}