icu_datetime/provider/pattern/runtime/
pattern.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#![allow(clippy::exhaustive_structs)] // part of data struct and internal API
6
7use super::super::{reference, PatternError, PatternItem, TimeGranularity};
8use alloc::vec::Vec;
9use core::str::FromStr;
10use icu_plurals::provider::FourBitMetadata;
11use icu_provider::prelude::*;
12use zerovec::{ZeroSlice, ZeroVec};
13
14/// A raw, low-level pattern for datetime formatting.
15///
16/// It consists of an owned-or-borrowed list of [`PatternItem`]s corresponding
17/// to either fields or literal characters.
18///
19/// <div class="stab unstable">
20/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
21/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
22/// to be stable, their Rust representation might not be. Use with caution.
23/// </div>
24#[derive(Debug, PartialEq, Eq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
25#[cfg_attr(feature = "datagen", derive(databake::Bake))]
26#[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider::pattern::runtime))]
27#[zerovec::make_varule(PatternULE)]
28#[zerovec::derive(Debug)]
29#[zerovec::skip_derive(Ord)]
30#[cfg_attr(feature = "serde", zerovec::derive(Deserialize))]
31#[cfg_attr(feature = "datagen", zerovec::derive(Serialize))]
32pub struct Pattern<'data> {
33    /// The list of [`PatternItem`]s.
34    pub items: ZeroVec<'data, PatternItem>,
35    /// Pre-computed metadata about the pattern.
36    ///
37    /// This field should contain the smallest time unit from the `items` vec.
38    /// If it doesn't, unexpected results for day periods may be encountered.
39    pub metadata: PatternMetadata,
40}
41
42/// Fully borrowed version of [`Pattern`].
43#[derive(Debug, Copy, Clone)]
44pub(crate) struct PatternBorrowed<'data> {
45    pub(crate) items: &'data ZeroSlice<PatternItem>,
46    pub(crate) metadata: PatternMetadata,
47}
48
49/// Metadata associated with a [`Pattern`].
50///
51/// <div class="stab unstable">
52/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
53/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
54/// to be stable, their Rust representation might not be. Use with caution.
55/// </div>
56#[derive(Debug, Copy, Clone, PartialEq, Eq)]
57#[zerovec::make_ule(PatternMetadataULE)]
58#[zerovec::skip_derive(Ord)]
59pub struct PatternMetadata(u8);
60
61impl PatternMetadata {
62    pub(crate) const DEFAULT: PatternMetadata = Self::from_time_granularity(TimeGranularity::None);
63
64    #[inline]
65    pub(crate) fn time_granularity(self) -> TimeGranularity {
66        TimeGranularity::from_ordinal(self.0)
67    }
68
69    pub(crate) fn from_items(items: &[PatternItem]) -> Self {
70        Self::from_iter_items(items.iter().copied())
71    }
72
73    pub(crate) fn from_iter_items(iter_items: impl Iterator<Item = PatternItem>) -> Self {
74        let time_granularity: TimeGranularity =
75            iter_items.map(Into::into).max().unwrap_or_default();
76        Self::from_time_granularity(time_granularity)
77    }
78
79    /// Merges the metadata from a date pattern and a time pattern into one.
80    #[inline]
81    pub(crate) fn merge_date_and_time_metadata(
82        _date: PatternMetadata,
83        time: PatternMetadata,
84    ) -> PatternMetadata {
85        // Currently we only have time granularity so we ignore the date metadata.
86        time
87    }
88
89    /// Creates a [`PatternMetadata`] from the [`TimeGranularity`] enum.
90    #[inline]
91    pub const fn from_time_granularity(time_granularity: TimeGranularity) -> Self {
92        Self(time_granularity.ordinal())
93    }
94
95    #[cfg(feature = "datagen")]
96    #[inline]
97    pub(crate) fn set_time_granularity(&mut self, time_granularity: TimeGranularity) {
98        self.0 = time_granularity.ordinal();
99    }
100
101    pub(crate) fn to_four_bit_metadata(self) -> FourBitMetadata {
102        #[allow(clippy::unwrap_used)] // valid values for self.0 are 0, 1, 2, 3, or 4
103        FourBitMetadata::try_from_byte(self.0).unwrap()
104    }
105
106    pub(crate) fn from_u8(other: u8) -> Self {
107        Self(TimeGranularity::from_ordinal(other).ordinal())
108    }
109}
110
111impl Default for PatternMetadata {
112    #[inline]
113    fn default() -> Self {
114        Self::DEFAULT
115    }
116}
117
118impl Pattern<'_> {
119    pub(crate) fn into_owned(self) -> Pattern<'static> {
120        Pattern {
121            items: self.items.into_owned(),
122            metadata: self.metadata,
123        }
124    }
125
126    pub(crate) fn as_borrowed(&self) -> PatternBorrowed {
127        PatternBorrowed {
128            items: &self.items,
129            metadata: self.metadata,
130        }
131    }
132
133    /// Borrows a [`Pattern`] from another [`Pattern`].
134    pub fn as_ref(&self) -> Pattern<'_> {
135        self.as_borrowed().as_pattern()
136    }
137}
138
139impl<'data> PatternBorrowed<'data> {
140    pub(crate) const DEFAULT: PatternBorrowed<'static> = PatternBorrowed {
141        items: ZeroSlice::new_empty(),
142        metadata: PatternMetadata::DEFAULT,
143    };
144
145    pub(crate) fn as_pattern(self) -> Pattern<'data> {
146        Pattern {
147            items: self.items.as_zerovec(),
148            metadata: self.metadata,
149        }
150    }
151}
152
153impl From<Vec<PatternItem>> for Pattern<'_> {
154    fn from(items: Vec<PatternItem>) -> Self {
155        Self {
156            metadata: PatternMetadata::from_items(&items),
157            items: ZeroVec::alloc_from_slice(&items),
158        }
159    }
160}
161
162impl FromIterator<PatternItem> for Pattern<'_> {
163    fn from_iter<T: IntoIterator<Item = PatternItem>>(iter: T) -> Self {
164        let items = iter.into_iter().collect::<ZeroVec<PatternItem>>();
165        Self {
166            metadata: PatternMetadata::from_iter_items(items.iter()),
167            items,
168        }
169    }
170}
171
172impl From<&reference::Pattern> for Pattern<'_> {
173    fn from(input: &reference::Pattern) -> Self {
174        Self {
175            items: ZeroVec::alloc_from_slice(&input.items),
176            metadata: PatternMetadata::from_time_granularity(input.time_granularity),
177        }
178    }
179}
180
181impl From<&Pattern<'_>> for reference::Pattern {
182    fn from(input: &Pattern<'_>) -> Self {
183        Self {
184            items: input.items.to_vec(),
185            time_granularity: input.metadata.time_granularity(),
186        }
187    }
188}
189
190impl FromStr for Pattern<'_> {
191    type Err = PatternError;
192
193    fn from_str(input: &str) -> Result<Self, Self::Err> {
194        let reference = reference::Pattern::from_str(input)?;
195        Ok(Self::from(&reference))
196    }
197}
198
199impl Default for Pattern<'_> {
200    fn default() -> Self {
201        Self {
202            items: ZeroVec::new(),
203            metadata: PatternMetadata::default(),
204        }
205    }
206}
207
208#[cfg(feature = "datagen")]
209impl databake::Bake for PatternMetadata {
210    fn bake(&self, ctx: &databake::CrateEnv) -> databake::TokenStream {
211        ctx.insert("icu_datetime");
212        let time_granularity = databake::Bake::bake(&self.time_granularity(), ctx);
213        databake::quote! {
214            icu_datetime::provider::pattern::runtime::PatternMetadata::from_time_granularity(#time_granularity)
215        }
216    }
217}
218
219#[cfg(feature = "datagen")]
220impl databake::BakeSize for PatternMetadata {
221    fn borrows_size(&self) -> usize {
222        0
223    }
224}
225
226#[test]
227#[cfg(feature = "datagen")]
228fn databake() {
229    databake::test_bake!(
230        PatternMetadata,
231        const,
232        crate::provider::pattern::runtime::PatternMetadata::from_time_granularity(
233            crate::provider::pattern::TimeGranularity::Hours
234        ),
235        icu_datetime,
236    );
237}