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

zbraniecki / icu4x / 6960837701

22 Nov 2023 02:34PM UTC coverage: 73.023% (+0.4%) from 72.607%
6960837701

push

github

web-flow
Put the units conversion info in a `ZeroTrie` (#4327)

8 of 11 new or added lines in 2 files covered. (72.73%)

1655 existing lines in 63 files now uncovered.

45357 of 62113 relevant lines covered (73.02%)

266109.06 hits per line

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

64.52
/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
#[cfg(doc)]
11
use crate::fields::{Hour, Weekday};
12

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

23
#[cfg(feature = "std")]
24
impl std::error::Error for LengthError {}
25

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

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

70
impl FieldLength {
71
    #[inline]
72
    pub(crate) fn idx(&self) -> u8 {
3,375✔
73
        match self {
3,375✔
74
            FieldLength::One => 1,
1,136✔
75
            FieldLength::TwoDigit => 2,
983✔
76
            FieldLength::Abbreviated => 3,
208✔
77
            FieldLength::Wide => 4,
874✔
78
            FieldLength::Narrow => 5,
109✔
79
            FieldLength::Six => 6,
57✔
UNCOV
80
            FieldLength::NumericOverride(o) => FIRST_NUMERIC_OVERRIDE
×
UNCOV
81
                .saturating_add(*o as u8)
×
82
                .min(FIRST_FIXED - 1),
83
            FieldLength::Fixed(p) => FIRST_FIXED.saturating_add(*p), /* truncate to at most 127 digits to avoid overflow */
8✔
84
        }
85
    }
3,375✔
86

87
    #[inline]
88
    pub(crate) fn from_idx(idx: u8) -> Result<Self, LengthError> {
16,831✔
89
        Ok(match idx {
33,660✔
90
            1 => Self::One,
7,736✔
91
            2 => Self::TwoDigit,
4,775✔
92
            3 => Self::Abbreviated,
1,092✔
93
            4 => Self::Wide,
2,617✔
94
            5 => Self::Narrow,
416✔
95
            6 => Self::Six,
143✔
96
            idx => {
52✔
97
                if idx < FIRST_NUMERIC_OVERRIDE {
52✔
98
                    return Err(LengthError::InvalidLength);
2✔
99
                }
100
                if idx < FIRST_FIXED {
50✔
UNCOV
101
                    Self::NumericOverride((idx - FIRST_NUMERIC_OVERRIDE).try_into()?)
×
102
                } else {
103
                    Self::Fixed(idx - FIRST_FIXED)
50✔
104
                }
105
            }
106
        })
107
    }
16,831✔
108

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

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

137
#[repr(transparent)]
UNCOV
138
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
×
UNCOV
139
pub struct FieldLengthULE(u8);
×
140

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

152
impl FieldLengthULE {
153
    #[inline]
154
    pub(crate) fn validate_byte(byte: u8) -> Result<(), ZeroVecError> {
120✔
155
        FieldLength::from_idx(byte)
120✔
156
            .map(|_| ())
120✔
UNCOV
157
            .map_err(|_| ZeroVecError::parse::<FieldLength>())
×
158
    }
120✔
159
}
160

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

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

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

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

229
impl fmt::Display for FieldNumericOverrides {
UNCOV
230
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
UNCOV
231
        self.as_str().fmt(f)
×
UNCOV
232
    }
×
233
}
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

© 2025 Coveralls, Inc