• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zbraniecki / icu4x / 9207498870

23 May 2024 07:11AM UTC coverage: 76.113% (-0.3%) from 76.402%
9207498870

push

github

web-flow
Add to `IsoDurationParser` documentation in `ixdtf` (#4916)

53397 of 70155 relevant lines covered (76.11%)

514353.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.27
/components/datetime/src/fields/length.rs
1
// This file is part of ICU4X. For terms of use, please see the file
2✔
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
use core::cmp::{Ord, PartialOrd};
6
use core::fmt;
7
use displaydoc::Display;
8
use zerovec::ule::{AsULE, ZeroVecError, ULE};
9

10
/// An error relating to the length of a field within a date pattern.
11
#[derive(Display, Debug, PartialEq, Copy, Clone)]
×
12
#[non_exhaustive]
13
pub enum LengthError {
14
    /// The length of the field string within the pattern is invalid, according to
15
    /// the field type and its supported field patterns in LDML. See [`FieldLength`].
16
    #[displaydoc("Invalid length")]
17
    InvalidLength,
18
}
19

20
#[cfg(feature = "std")]
21
impl std::error::Error for LengthError {}
22

23
/// An enum representing the length of a field within a date or time formatting pattern string,
24
/// in which the pattern field is represented as a letter occurring 1 or more times in a row, ex:
25
/// `MMM`, `dd`, `y`.  See the
26
/// [LDML documentation in UTS 35](https://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns)
27
/// for more details.
28
#[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd)]
1,990,186✔
29
#[cfg_attr(
30
    feature = "datagen",
31
    derive(serde::Serialize, databake::Bake),
27,697✔
32
    databake(path = icu_datetime::fields),
33
)]
34
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
110,768✔
35
#[allow(clippy::exhaustive_enums)] // part of data struct
36
pub enum FieldLength {
27,692✔
37
    /// Typical style is 1-2 digits. For numeric-only fields.
38
    One,
39
    /// Typical style is 2 digits. For numeric-only fields.
40
    TwoDigit,
41
    /// Abbreviated (spellout) format.
42
    Abbreviated,
43
    /// Wide / Long / Full  (spellout) format.
44
    Wide,
45
    /// Narrow / Long / Full  (spellout) format.
46
    Narrow,
47
    /// Meaning is field-dependent, for patterns that are 6 characters long. Ex: a [`Weekday`](super::Weekday) pattern like
48
    /// `EEEEEE` means "Short", but `jjjjjj` or `CCCCCC` for [`Hour`](super::Hour) may mean
49
    /// "Numeric hour (2 digits, zero pad if needed), narrow dayPeriod if used". See the
50
    /// [LDML documentation in UTS 35](https://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns)
51
    /// for more details.
52
    Six,
53
    /// A fixed size format for numeric-only fields that is at most 127 digits.
54
    Fixed(u8),
92✔
55
    /// FieldLength::One (numeric), but overridden with a different numbering system
56
    NumericOverride(FieldNumericOverrides),
72✔
57
}
58

59
/// First index used for numeric overrides in compact FieldLength representation
60
///
61
/// Currently 17 due to decision in <https://unicode-org.atlassian.net/browse/CLDR-17217>,
62
/// may become 16 if the `> 16` is updated to a ` >= 16`
63
const FIRST_NUMERIC_OVERRIDE: u8 = 17;
64
/// First index used for fixed size formats in compact FieldLength representation
65
const FIRST_FIXED: u8 = 128;
66

67
impl FieldLength {
68
    #[inline]
69
    pub(crate) fn idx(&self) -> u8 {
1,120,109✔
70
        match self {
1,120,109✔
71
            FieldLength::One => 1,
651,024✔
72
            FieldLength::TwoDigit => 2,
314,427✔
73
            FieldLength::Abbreviated => 3,
80,688✔
74
            FieldLength::Wide => 4,
67,572✔
75
            FieldLength::Narrow => 5,
6,313✔
76
            FieldLength::Six => 6,
57✔
77
            FieldLength::NumericOverride(o) => FIRST_NUMERIC_OVERRIDE
36✔
78
                .saturating_add(*o as u8)
18✔
79
                .min(FIRST_FIXED - 1),
80
            FieldLength::Fixed(p) => FIRST_FIXED.saturating_add(*p), /* truncate to at most 127 digits to avoid overflow */
10✔
81
        }
82
    }
1,120,109✔
83

84
    #[inline]
85
    pub(crate) fn from_idx(idx: u8) -> Result<Self, LengthError> {
2,396,502✔
86
        Ok(match idx {
4,793,002✔
87
            1 => Self::One,
1,652,591✔
88
            2 => Self::TwoDigit,
399,826✔
89
            3 => Self::Abbreviated,
178,403✔
90
            4 => Self::Wide,
155,377✔
91
            5 => Self::Narrow,
9,957✔
92
            6 => Self::Six,
203✔
93
            idx => {
145✔
94
                if idx < FIRST_NUMERIC_OVERRIDE {
145✔
95
                    return Err(LengthError::InvalidLength);
2✔
96
                }
97
                if idx < FIRST_FIXED {
143✔
98
                    Self::NumericOverride((idx - FIRST_NUMERIC_OVERRIDE).try_into()?)
54✔
99
                } else {
100
                    Self::Fixed(idx - FIRST_FIXED)
89✔
101
                }
102
            }
103
        })
104
    }
2,396,502✔
105

106
    #[inline]
107
    #[cfg(feature = "datagen")]
108
    pub(crate) fn to_len(self) -> usize {
221✔
109
        match self {
221✔
110
            FieldLength::One => 1,
118✔
111
            FieldLength::TwoDigit => 2,
46✔
112
            FieldLength::Abbreviated => 3,
17✔
113
            FieldLength::Wide => 4,
18✔
114
            FieldLength::Narrow => 5,
13✔
115
            FieldLength::Six => 6,
2✔
116
            FieldLength::NumericOverride(o) => FIRST_NUMERIC_OVERRIDE as usize + o as usize,
×
117
            FieldLength::Fixed(p) => p as usize,
7✔
118
        }
119
    }
221✔
120

121
    /// UTS 35 defines several 1 and 2 symbols to be the same as 3 symbols (abbreviated).
122
    /// For example, 'a' represents an abbreviated day period, the same as 'aaa'.
123
    ///
124
    /// This function maps field lengths 1 and 2 to field length 3.
125
    #[cfg(feature = "experimental")]
126
    pub(crate) fn numeric_to_abbr(self) -> Self {
4,675✔
127
        match self {
4,675✔
128
            FieldLength::One | FieldLength::TwoDigit => FieldLength::Abbreviated,
247✔
129
            other => other,
4,428✔
130
        }
131
    }
4,675✔
132
}
133

134
#[repr(transparent)]
135
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
×
136
pub struct FieldLengthULE(u8);
×
137

138
impl AsULE for FieldLength {
139
    type ULE = FieldLengthULE;
140
    fn to_unaligned(self) -> Self::ULE {
6✔
141
        FieldLengthULE(self.idx())
6✔
142
    }
6✔
143
    fn from_unaligned(unaligned: Self::ULE) -> Self {
4✔
144
        #[allow(clippy::unwrap_used)] // OK because the ULE is pre-validated
145
        Self::from_idx(unaligned.0).unwrap()
4✔
146
    }
4✔
147
}
148

149
impl FieldLengthULE {
150
    #[inline]
151
    pub(crate) fn validate_byte(byte: u8) -> Result<(), ZeroVecError> {
81,260✔
152
        FieldLength::from_idx(byte)
81,260✔
153
            .map(|_| ())
81,259✔
154
            .map_err(|_| ZeroVecError::parse::<FieldLength>())
×
155
    }
81,260✔
156
}
157

158
// Safety checklist for ULE:
159
//
160
// 1. Must not include any uninitialized or padding bytes (true since transparent over a ULE).
161
// 2. Must have an alignment of 1 byte (true since transparent over a ULE).
162
// 3. ULE::validate_byte_slice() checks that the given byte slice represents a valid slice.
163
// 4. ULE::validate_byte_slice() checks that the given byte slice has a valid length
164
//    (true since transparent over a type of size 1).
165
// 5. All other methods must be left with their default impl.
166
// 6. Byte equality is semantic equality.
167
unsafe impl ULE for FieldLengthULE {
168
    fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> {
2✔
169
        for byte in bytes {
4✔
170
            Self::validate_byte(*byte)?;
2✔
171
        }
172
        Ok(())
2✔
173
    }
2✔
174
}
175

176
/// Various numeric overrides for datetime patterns
177
/// as found in CLDR
178
#[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd)]
18✔
179
#[cfg_attr(
180
    feature = "datagen",
181
    derive(serde::Serialize, databake::Bake),
×
182
    databake(path = icu_datetime::fields),
183
)]
184
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
×
185
#[non_exhaustive]
186
pub enum FieldNumericOverrides {
×
187
    /// `hanidec`
188
    Hanidec = 0,
189
    /// `hanidays`
190
    Hanidays = 1,
191
    /// `hebr`
192
    Hebr = 2,
193
    /// `romanlow`
194
    Romanlow = 3,
195
    /// `jpnyear`
196
    Jpnyear = 4,
197
}
198

199
impl TryFrom<u8> for FieldNumericOverrides {
200
    type Error = LengthError;
201
    fn try_from(other: u8) -> Result<Self, LengthError> {
54✔
202
        Ok(match other {
108✔
203
            0 => Self::Hanidec,
36✔
204
            1 => Self::Hanidays,
×
205
            2 => Self::Hebr,
×
206
            3 => Self::Romanlow,
×
207
            4 => Self::Jpnyear,
18✔
208
            _ => return Err(LengthError::InvalidLength),
×
209
        })
210
    }
54✔
211
}
212

213
impl FieldNumericOverrides {
214
    /// Convert this to the corresponding string code
215
    pub fn as_str(self) -> &'static str {
×
216
        match self {
×
217
            Self::Hanidec => "hanidec",
×
218
            Self::Hanidays => "hanidays",
×
219
            Self::Hebr => "hebr",
×
220
            Self::Romanlow => "romanlow",
×
221
            Self::Jpnyear => "jpnyear",
×
222
        }
223
    }
×
224
}
225

226
impl fmt::Display for FieldNumericOverrides {
227
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
228
        self.as_str().fmt(f)
×
229
    }
×
230
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc