icu_datetime/provider/fields/
mod.rs1#![allow(clippy::exhaustive_structs)] mod length;
10pub(crate) mod symbols;
11
12use displaydoc::Display;
13pub use length::{FieldLength, FieldNumericOverrides, LengthError};
14pub use symbols::*;
15use writeable::Writeable;
16
17#[cfg(any(feature = "experimental", feature = "datagen"))]
18pub mod components;
19
20use core::{
21 cmp::{Ord, PartialOrd},
22 convert::TryFrom,
23};
24
25use crate::error::ErrorField;
26
27#[derive(Display, Debug, Copy, Clone, PartialEq)]
33#[non_exhaustive]
34pub enum Error {
35 #[displaydoc("Field {0:?} is not a valid length")]
37 InvalidLength(FieldSymbol),
38}
39
40impl core::error::Error for Error {}
41
42#[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd)]
48#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
49#[cfg_attr(feature = "datagen", databake(path = icu_datetime::fields))]
50#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
51#[zerovec::make_ule(FieldULE)]
52pub struct Field {
53 pub symbol: FieldSymbol,
56 pub length: FieldLength,
59}
60
61impl Field {
62 #[cfg(feature = "datagen")]
63 pub(crate) fn get_length_type(self) -> TextOrNumeric {
64 match self.symbol {
65 FieldSymbol::Era => TextOrNumeric::Text,
66 FieldSymbol::Year(year) => year.get_length_type(self.length),
67 FieldSymbol::Month(month) => month.get_length_type(self.length),
68 FieldSymbol::Week(week) => week.get_length_type(self.length),
69 FieldSymbol::Day(day) => day.get_length_type(self.length),
70 FieldSymbol::Weekday(weekday) => weekday.get_length_type(self.length),
71 FieldSymbol::DayPeriod(day_period) => day_period.get_length_type(self.length),
72 FieldSymbol::Hour(hour) => hour.get_length_type(self.length),
73 FieldSymbol::Minute => TextOrNumeric::Numeric,
74 FieldSymbol::Second(second) => second.get_length_type(self.length),
75 FieldSymbol::TimeZone(zone) => zone.get_length_type(self.length),
76 FieldSymbol::DecimalSecond(_) => TextOrNumeric::Numeric,
77 }
78 }
79}
80
81impl Writeable for Field {
82 fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
83 let ch: char = self.symbol.into();
84 for _ in 0..self.length.to_len() {
85 sink.write_char(ch)?;
86 }
87 Ok(())
88 }
89 fn writeable_length_hint(&self) -> writeable::LengthHint {
90 writeable::LengthHint::exact(self.length.to_len())
91 }
92}
93
94writeable::impl_display_with_writeable!(Field);
95
96impl FieldULE {
97 #[inline]
98 pub(crate) fn validate_byte_pair(bytes: (u8, u8)) -> Result<(), zerovec::ule::UleError> {
99 symbols::FieldSymbolULE::validate_byte(bytes.0)?;
100 length::FieldLengthULE::validate_byte(bytes.1)?;
101 Ok(())
102 }
103}
104
105impl From<(FieldSymbol, FieldLength)> for Field {
106 fn from(input: (FieldSymbol, FieldLength)) -> Self {
107 Self {
108 symbol: input.0,
109 length: input.1,
110 }
111 }
112}
113
114impl TryFrom<(FieldSymbol, usize)> for Field {
115 type Error = Error;
116 fn try_from(input: (FieldSymbol, usize)) -> Result<Self, Self::Error> {
117 let length = FieldLength::from_idx(input.1 as u8)
118 .map_err(|_| Self::Error::InvalidLength(input.0))?;
119 Ok(Self {
120 symbol: input.0,
121 length,
122 })
123 }
124}
125
126impl From<ErrorField> for Field {
127 fn from(value: ErrorField) -> Self {
129 value.0
130 }
131}
132
133impl From<Field> for ErrorField {
134 fn from(value: Field) -> Self {
136 Self(value)
137 }
138}
139
140#[cfg(test)]
141mod test {
142 use super::*;
143 use crate::provider::fields::{Field, FieldLength, FieldSymbol, Second, Year};
144 use zerovec::ule::{AsULE, ULE};
145
146 #[test]
147 fn test_field_as_ule() {
148 let samples = [
149 (
150 Field::from((FieldSymbol::Minute, FieldLength::Two)),
151 [FieldSymbol::Minute.idx(), FieldLength::Two.idx()],
152 ),
153 (
154 Field::from((FieldSymbol::Year(Year::Calendar), FieldLength::Four)),
155 [
156 FieldSymbol::Year(Year::Calendar).idx(),
157 FieldLength::Four.idx(),
158 ],
159 ),
160 (
161 Field::from((FieldSymbol::Year(Year::Cyclic), FieldLength::Four)),
162 [
163 FieldSymbol::Year(Year::Cyclic).idx(),
164 FieldLength::Four.idx(),
165 ],
166 ),
167 (
168 Field::from((FieldSymbol::Second(Second::MillisInDay), FieldLength::One)),
169 [
170 FieldSymbol::Second(Second::MillisInDay).idx(),
171 FieldLength::One.idx(),
172 ],
173 ),
174 ];
175
176 for (ref_field, ref_bytes) in samples {
177 let ule = ref_field.to_unaligned();
178 assert_eq!(ULE::slice_as_bytes(&[ule]), ref_bytes);
179 let field = Field::from_unaligned(ule);
180 assert_eq!(field, ref_field);
181 }
182 }
183
184 #[test]
185 fn test_field_ule() {
186 let samples = [(
187 [
188 Field::from((FieldSymbol::Year(Year::Calendar), FieldLength::Four)),
189 Field::from((FieldSymbol::Second(Second::MillisInDay), FieldLength::One)),
190 ],
191 [
192 [
193 FieldSymbol::Year(Year::Calendar).idx(),
194 FieldLength::Four.idx(),
195 ],
196 [
197 FieldSymbol::Second(Second::MillisInDay).idx(),
198 FieldLength::One.idx(),
199 ],
200 ],
201 )];
202
203 for (ref_field, ref_bytes) in samples {
204 let mut bytes: Vec<u8> = vec![];
205 for item in ref_field.iter() {
206 let ule = item.to_unaligned();
207 bytes.extend(ULE::slice_as_bytes(&[ule]));
208 }
209
210 let mut bytes2: Vec<u8> = vec![];
211 for seq in ref_bytes.iter() {
212 bytes2.extend_from_slice(seq);
213 }
214
215 assert!(FieldULE::validate_bytes(&bytes).is_ok());
216 assert_eq!(bytes, bytes2);
217 }
218 }
219}