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

zbraniecki / icu4x / 12020603084

23 Nov 2024 08:43PM UTC coverage: 75.71% (+0.2%) from 75.477%
12020603084

push

github

sffc
Touch Cargo.lock

55589 of 73424 relevant lines covered (75.71%)

644270.14 hits per line

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

55.1
/components/datetime/src/fields/length.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
use core::cmp::{Ord, PartialOrd};
6
use core::fmt;
7
use displaydoc::Display;
8
use zerovec::ule::{AsULE, UleError, 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
///
25
/// Such strings represent fields as a letter occurring 1 or more times in a row, ex:
26
/// `MMM`, `dd`, `y`.  See the
27
/// [LDML documentation in UTS 35](https://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns)
28
/// for more details.
29
#[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd)]
1,248,775✔
30
#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
10✔
31
#[cfg_attr(feature = "datagen", databake(path = icu_datetime::fields))]
32
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
20✔
33
#[allow(clippy::exhaustive_enums)] // part of data struct
34
pub enum FieldLength {
5✔
35
    /// Numeric: minimum digits
36
    ///
37
    /// Text: same as [`Self::Three`]
38
    One,
×
39
    /// Numeric: pad to 2 digits
40
    ///
41
    /// Text: same as [`Self::Three`]
42
    Two,
×
43
    /// Numeric: pad to 3 digits
44
    ///
45
    /// Text: Abbreviated format.
46
    Three,
×
47
    /// Numeric: pad to 4 digits
48
    ///
49
    /// Text: Wide format.
50
    Four,
×
51
    /// Numeric: pad to 5 digits
52
    ///
53
    /// Text: Narrow format.
54
    Five,
×
55
    /// Numeric: pad to 6 digits
56
    ///
57
    /// Text: Short format.
58
    Six,
×
59
    /// FieldLength::One (numeric), but overridden with a different numbering system
60
    NumericOverride(FieldNumericOverrides),
×
61
}
62

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

71
impl FieldLength {
72
    #[inline]
73
    pub(crate) fn idx(&self) -> u8 {
701,392✔
74
        match self {
701,392✔
75
            FieldLength::One => 1,
411,783✔
76
            FieldLength::Two => 2,
180,604✔
77
            FieldLength::Three => 3,
59,613✔
78
            FieldLength::Four => 4,
35,340✔
79
            FieldLength::Five => 5,
14,047✔
80
            FieldLength::Six => 6,
5✔
81
            FieldLength::NumericOverride(o) => FIRST_NUMERIC_OVERRIDE
×
82
                .saturating_add(*o as u8)
×
83
                .min(LAST_NUMERIC_OVERRIDE),
84
        }
85
    }
701,392✔
86

87
    #[inline]
88
    pub(crate) fn from_idx(idx: u8) -> Result<Self, LengthError> {
1,495,027✔
89
        Ok(match idx {
2,990,052✔
90
            1 => Self::One,
947,976✔
91
            2 => Self::Two,
237,213✔
92
            3 => Self::Three,
146,979✔
93
            4 => Self::Four,
142,560✔
94
            5 => Self::Five,
20,281✔
95
            6 => Self::Six,
16✔
96
            idx if (FIRST_NUMERIC_OVERRIDE..=LAST_NUMERIC_OVERRIDE).contains(&idx) => {
2✔
97
                Self::NumericOverride((idx - FIRST_NUMERIC_OVERRIDE).try_into()?)
1,495,027✔
98
            }
×
99
            _ => return Err(LengthError::InvalidLength),
2✔
100
        })
101
    }
1,495,027✔
102

103
    #[inline]
104
    pub(crate) fn to_len(self) -> usize {
3,344✔
105
        match self {
3,344✔
106
            FieldLength::One => 1,
1,771✔
107
            FieldLength::Two => 2,
1,496✔
108
            FieldLength::Three => 3,
33✔
109
            FieldLength::Four => 4,
31✔
110
            FieldLength::Five => 5,
11✔
111
            FieldLength::Six => 6,
2✔
112
            FieldLength::NumericOverride(o) => FIRST_NUMERIC_OVERRIDE as usize + o as usize,
×
113
        }
114
    }
3,344✔
115

116
    /// UTS 35 defines several 1 and 2 symbols to be the same as 3 symbols (abbreviated).
117
    /// For example, 'a' represents an abbreviated day period, the same as 'aaa'.
118
    ///
119
    /// This function maps field lengths 1 and 2 to field length 3.
120
    pub(crate) fn numeric_to_abbr(self) -> Self {
6,575✔
121
        match self {
6,575✔
122
            FieldLength::One | FieldLength::Two => FieldLength::Three,
1,053✔
123
            other => other,
5,522✔
124
        }
125
    }
6,575✔
126
}
127

128
#[repr(transparent)]
129
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
×
130
pub struct FieldLengthULE(u8);
×
131

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

143
impl FieldLengthULE {
144
    #[inline]
145
    pub(crate) fn validate_byte(byte: u8) -> Result<(), UleError> {
40,878✔
146
        FieldLength::from_idx(byte)
40,878✔
147
            .map(|_| ())
40,879✔
148
            .map_err(|_| UleError::parse::<FieldLength>())
×
149
    }
40,878✔
150
}
151

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

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

190
impl TryFrom<u8> for FieldNumericOverrides {
191
    type Error = LengthError;
192
    fn try_from(other: u8) -> Result<Self, LengthError> {
×
193
        Ok(match other {
×
194
            0 => Self::Hanidec,
×
195
            1 => Self::Hanidays,
×
196
            2 => Self::Hebr,
×
197
            3 => Self::Romanlow,
×
198
            4 => Self::Jpnyear,
×
199
            _ => return Err(LengthError::InvalidLength),
×
200
        })
201
    }
×
202
}
203

204
impl FieldNumericOverrides {
205
    /// Convert this to the corresponding string code
206
    pub fn as_str(self) -> &'static str {
×
207
        match self {
×
208
            Self::Hanidec => "hanidec",
×
209
            Self::Hanidays => "hanidays",
×
210
            Self::Hebr => "hebr",
×
211
            Self::Romanlow => "romanlow",
×
212
            Self::Jpnyear => "jpnyear",
×
213
        }
214
    }
×
215
}
216

217
impl fmt::Display for FieldNumericOverrides {
218
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
219
        self.as_str().fmt(f)
×
220
    }
×
221
}
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