jiff/error/tz/
zic.rs

1#[cfg(not(test))]
2pub(crate) use self::disabled::*;
3#[cfg(test)]
4pub(crate) use self::enabled::*;
5
6#[cfg(not(test))]
7mod disabled {
8    #[derive(Clone, Debug)]
9    pub(crate) enum Error {}
10
11    impl core::fmt::Display for Error {
12        fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
13            unreachable!()
14        }
15    }
16}
17
18#[cfg(test)]
19mod enabled {
20    use alloc::boxed::Box;
21
22    use crate::error;
23
24    // `man zic` says that the max line length including the line
25    // terminator is 2048. The `core::str::Lines` iterator doesn't include
26    // the terminator, so we subtract 1 to account for that. Note that this
27    // could potentially allow one extra byte in the case of a \r\n line
28    // terminator, but this seems fine.
29    pub(crate) const MAX_LINE_LEN: usize = 2047;
30
31    #[derive(Clone, Debug)]
32    pub(crate) enum Error {
33        DuplicateLink { name: Box<str> },
34        DuplicateLinkZone { name: Box<str> },
35        DuplicateZone { name: Box<str> },
36        DuplicateZoneLink { name: Box<str> },
37        ExpectedCloseQuote,
38        ExpectedColonAfterHour,
39        ExpectedColonAfterMinute,
40        ExpectedContinuationZoneThreeFields,
41        ExpectedContinuationZoneLine { name: Box<str> },
42        ExpectedDotAfterSeconds,
43        ExpectedFirstZoneFourFields,
44        ExpectedLinkTwoFields,
45        ExpectedMinuteAfterHours,
46        ExpectedNameBegin,
47        ExpectedNanosecondDigits,
48        ExpectedNonEmptyAbbreviation,
49        ExpectedNonEmptyAt,
50        ExpectedNonEmptyName,
51        ExpectedNonEmptySave,
52        ExpectedNonEmptyZoneName,
53        ExpectedNothingAfterTime,
54        ExpectedRuleNineFields { got: usize },
55        ExpectedSecondAfterMinutes,
56        ExpectedTimeOneHour,
57        ExpectedUntilYear,
58        ExpectedWhitespaceAfterQuotedField,
59        ExpectedZoneNameComponentNoDots { component: Box<str> },
60        FailedContinuationZone,
61        FailedLinkLine,
62        FailedParseDay,
63        FailedParseFieldAt,
64        FailedParseFieldFormat,
65        FailedParseFieldFrom,
66        FailedParseFieldIn,
67        FailedParseFieldLetters,
68        FailedParseFieldLinkName,
69        FailedParseFieldLinkTarget,
70        FailedParseFieldName,
71        FailedParseFieldOn,
72        FailedParseFieldRules,
73        FailedParseFieldSave,
74        FailedParseFieldStdOff,
75        FailedParseFieldTo,
76        FailedParseFieldUntil,
77        FailedParseHour,
78        FailedParseMinute,
79        FailedParseMonth,
80        FailedParseNanosecond,
81        FailedParseSecond,
82        FailedParseTimeDuration,
83        FailedParseYear,
84        FailedRule { name: Box<str> },
85        FailedRuleLine,
86        FailedZoneFirst,
87        Line { number: usize },
88        LineMaxLength,
89        LineNul,
90        LineOverflow,
91        InvalidAbbreviation,
92        InvalidRuleYear { start: i16, end: i16 },
93        InvalidUtf8,
94        UnrecognizedAtTimeSuffix,
95        UnrecognizedDayOfMonthFormat,
96        UnrecognizedDayOfWeek,
97        UnrecognizedMonthName,
98        UnrecognizedSaveTimeSuffix,
99        UnrecognizedTrailingTimeDuration,
100        UnrecognizedZicLine,
101    }
102
103    impl From<Error> for error::Error {
104        #[cold]
105        #[inline(never)]
106        fn from(err: Error) -> error::Error {
107            error::ErrorKind::TzZic(err).into()
108        }
109    }
110
111    impl error::IntoError for Error {
112        fn into_error(self) -> error::Error {
113            self.into()
114        }
115    }
116
117    impl core::fmt::Display for Error {
118        fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
119            use self::Error::*;
120
121            match *self {
122                DuplicateLink { ref name } => {
123                    write!(f, "found duplicate link with name `{name}`")
124                }
125                DuplicateLinkZone { ref name } => write!(
126                    f,
127                    "found link with name `{name}` that conflicts \
128                     with a zone of the same name",
129                ),
130                DuplicateZone { ref name } => {
131                    write!(f, "found duplicate zone with name `{name}`")
132                }
133                DuplicateZoneLink { ref name } => write!(
134                    f,
135                    "found zone with name `{name}` that conflicts \
136                     with a link of the same name",
137                ),
138                ExpectedCloseQuote => {
139                    f.write_str("found unclosed quote for field")
140                }
141                ExpectedColonAfterHour => {
142                    f.write_str("expected `:` after hours")
143                }
144                ExpectedColonAfterMinute => {
145                    f.write_str("expected `:` after minutes")
146                }
147                ExpectedContinuationZoneLine { ref name } => write!(
148                    f,
149                    "expected continuation zone line for `{name}`, \
150                     but found end of data instead",
151                ),
152                ExpectedContinuationZoneThreeFields => f.write_str(
153                    "expected continuation `ZONE` line \
154                     to have at least 3 fields",
155                ),
156                ExpectedDotAfterSeconds => {
157                    f.write_str("expected `.` after seconds")
158                }
159                ExpectedFirstZoneFourFields => f.write_str(
160                    "expected first `ZONE` line to have at least 4 fields",
161                ),
162                ExpectedLinkTwoFields => {
163                    f.write_str("expected exactly 2 fields after `LINK`")
164                }
165                ExpectedMinuteAfterHours => {
166                    f.write_str("expected minute digits after `HH:`")
167                }
168                ExpectedNameBegin => f.write_str(
169                    "`NAME` field cannot begin with a digit, `+` or `-`, \
170                     but found `NAME` that begins with one of those",
171                ),
172                ExpectedNanosecondDigits => {
173                    f.write_str("expected nanosecond digits after `HH:MM:SS.`")
174                }
175                ExpectedNonEmptyAbbreviation => f.write_str(
176                    "empty time zone abbreviations are not allowed",
177                ),
178                ExpectedNonEmptyAt => {
179                    f.write_str("`AT` field for rule cannot be empty")
180                }
181                ExpectedNonEmptyName => {
182                    f.write_str("`NAME` field for rule cannot be empty")
183                }
184                ExpectedNonEmptySave => {
185                    f.write_str("`SAVE` field for rule cannot be empty")
186                }
187                ExpectedNonEmptyZoneName => {
188                    f.write_str("zone names cannot be empty")
189                }
190                ExpectedNothingAfterTime => f.write_str(
191                    "expected no more fields after time of day, \
192                     but found at least one",
193                ),
194                ExpectedRuleNineFields { got } => write!(
195                    f,
196                    "expected exactly 9 fields for rule, \
197                     but found {got} fields",
198                ),
199                ExpectedSecondAfterMinutes => {
200                    f.write_str("expected second digits after `HH:MM:`")
201                }
202                ExpectedTimeOneHour => f.write_str(
203                    "expected time duration to contain \
204                     at least one hour digit",
205                ),
206                ExpectedUntilYear => f.write_str("expected at least a year"),
207                ExpectedWhitespaceAfterQuotedField => {
208                    f.write_str("expected whitespace after quoted field")
209                }
210                ExpectedZoneNameComponentNoDots { ref component } => write!(
211                    f,
212                    "component `{component}` in zone name cannot \
213                     be \".\" or \"..\"",
214                ),
215                FailedContinuationZone => {
216                    f.write_str("failed to parse continuation `Zone` line")
217                }
218                FailedLinkLine => f.write_str("failed to parse `Link` line"),
219                FailedParseDay => f.write_str("failed to parse day"),
220                FailedParseFieldAt => {
221                    f.write_str("failed to parse `NAME` field")
222                }
223                FailedParseFieldFormat => {
224                    f.write_str("failed to parse `FORMAT` field")
225                }
226                FailedParseFieldFrom => {
227                    f.write_str("failed to parse `FROM` field")
228                }
229                FailedParseFieldIn => {
230                    f.write_str("failed to parse `IN` field")
231                }
232                FailedParseFieldLetters => {
233                    f.write_str("failed to parse `LETTERS` field")
234                }
235                FailedParseFieldLinkName => {
236                    f.write_str("failed to parse `LINK` name field")
237                }
238                FailedParseFieldLinkTarget => {
239                    f.write_str("failed to parse `LINK` target field")
240                }
241                FailedParseFieldName => {
242                    f.write_str("failed to parse `NAME` field")
243                }
244                FailedParseFieldOn => {
245                    f.write_str("failed to parse `ON` field")
246                }
247                FailedParseFieldRules => {
248                    f.write_str("failed to parse `RULES` field")
249                }
250                FailedParseFieldSave => {
251                    f.write_str("failed to parse `SAVE` field")
252                }
253                FailedParseFieldStdOff => {
254                    f.write_str("failed to parse `STDOFF` field")
255                }
256                FailedParseFieldTo => {
257                    f.write_str("failed to parse `TO` field")
258                }
259                FailedParseFieldUntil => {
260                    f.write_str("failed to parse `UNTIL` field")
261                }
262                FailedParseHour => f.write_str("failed to parse hour"),
263                FailedParseMinute => f.write_str("failed to parse minute"),
264                FailedParseMonth => f.write_str("failed to parse month"),
265                FailedParseNanosecond => {
266                    f.write_str("failed to parse nanosecond")
267                }
268                FailedParseSecond => f.write_str("failed to parse second"),
269                FailedParseTimeDuration => {
270                    f.write_str("failed to parse time duration")
271                }
272                FailedParseYear => f.write_str("failed to parse year"),
273                FailedRule { name: ref rule } => {
274                    write!(f, "failed to parse rule `{rule}`")
275                }
276                FailedRuleLine => f.write_str("failed to parse `Rule` line"),
277                FailedZoneFirst => {
278                    f.write_str("failed to parse first `Zone` line")
279                }
280                InvalidAbbreviation => f.write_str(
281                    "time zone abbreviation \
282                     contains invalid character; only \"+\", \"-\" and \
283                     ASCII alpha-numeric characters are allowed",
284                ),
285                InvalidRuleYear { start, end } => write!(
286                    f,
287                    "found start year={start} \
288                     to be greater than end year={end}"
289                ),
290                InvalidUtf8 => f.write_str("invalid UTF-8"),
291                Line { number } => write!(f, "line {number}"),
292                LineMaxLength => write!(
293                    f,
294                    "found line with length that exceeds \
295                     max length of {MAX_LINE_LEN}",
296                ),
297                LineNul => f.write_str(
298                    "found line with NUL byte, which isn't allowed",
299                ),
300                LineOverflow => f.write_str("line count overflowed"),
301                UnrecognizedAtTimeSuffix => {
302                    f.write_str("unrecognized `AT` time suffix")
303                }
304                UnrecognizedDayOfMonthFormat => {
305                    f.write_str("unrecognized format for day-of-month")
306                }
307                UnrecognizedDayOfWeek => {
308                    f.write_str("unrecognized day of the week")
309                }
310                UnrecognizedMonthName => {
311                    f.write_str("unrecognized month name")
312                }
313                UnrecognizedSaveTimeSuffix => {
314                    f.write_str("unrecognized `SAVE` time suffix")
315                }
316                UnrecognizedTrailingTimeDuration => {
317                    f.write_str("found unrecognized suffix in time duration")
318                }
319                UnrecognizedZicLine => f.write_str("unrecognized zic line"),
320            }
321        }
322    }
323}