nt_time/
error.rs

1// SPDX-FileCopyrightText: 2023 Shun Sakai
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! Error types for this crate.
6
7use core::{
8    error::Error,
9    fmt,
10    num::{IntErrorKind, ParseIntError},
11};
12
13/// The error type indicating that a [`FileTime`](crate::FileTime) was out of
14/// range.
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub struct FileTimeRangeError(FileTimeRangeErrorKind);
17
18impl FileTimeRangeError {
19    pub(crate) const fn new(kind: FileTimeRangeErrorKind) -> Self {
20        Self(kind)
21    }
22
23    /// Returns the corresponding [`FileTimeRangeErrorKind`] for this error.
24    ///
25    /// # Examples
26    ///
27    /// ```
28    /// # use nt_time::{FileTime, error::FileTimeRangeErrorKind};
29    /// #
30    /// let err = FileTime::from_unix_time_secs(i64::MIN).unwrap_err();
31    /// assert_eq!(err.kind(), FileTimeRangeErrorKind::Negative);
32    ///
33    /// let err = FileTime::from_unix_time_secs(i64::MAX).unwrap_err();
34    /// assert_eq!(err.kind(), FileTimeRangeErrorKind::Overflow);
35    /// ```
36    #[must_use]
37    pub const fn kind(&self) -> FileTimeRangeErrorKind {
38        self.0
39    }
40}
41
42impl fmt::Display for FileTimeRangeError {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        self.kind().fmt(f)
45    }
46}
47
48impl Error for FileTimeRangeError {}
49
50impl From<FileTimeRangeErrorKind> for FileTimeRangeError {
51    fn from(kind: FileTimeRangeErrorKind) -> Self {
52        Self::new(kind)
53    }
54}
55
56/// Details of the error that caused a [`FileTimeRangeError`].
57#[derive(Clone, Copy, Debug, Eq, PartialEq)]
58pub enum FileTimeRangeErrorKind {
59    /// Value was negative.
60    ///
61    /// This means the file time was before "1601-01-01 00:00:00 UTC".
62    Negative,
63
64    /// Value was too big to be represented as [`FileTime`](crate::FileTime).
65    ///
66    /// This means the file time was after "+60056-05-28 05:36:10.955161500
67    /// UTC".
68    Overflow,
69}
70
71impl fmt::Display for FileTimeRangeErrorKind {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        match self {
74            Self::Negative => write!(f, "file time is before `1601-01-01 00:00:00 UTC`"),
75            Self::Overflow => write!(
76                f,
77                "file time is after `+60056-05-28 05:36:10.955161500 UTC`"
78            ),
79        }
80    }
81}
82
83/// An error which can be returned when parsing a [`FileTime`](crate::FileTime).
84#[derive(Clone, Debug, Eq, PartialEq)]
85pub struct ParseFileTimeError(ParseIntError);
86
87impl ParseFileTimeError {
88    pub(crate) const fn new(inner: ParseIntError) -> Self {
89        Self(inner)
90    }
91}
92
93impl fmt::Display for ParseFileTimeError {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        let inner = &self.0;
96        if inner.kind() == &IntErrorKind::PosOverflow {
97            write!(
98                f,
99                "file time is after `+60056-05-28 05:36:10.955161500 UTC`"
100            )
101        } else {
102            inner.fmt(f)
103        }
104    }
105}
106
107impl Error for ParseFileTimeError {
108    fn source(&self) -> Option<&(dyn Error + 'static)> {
109        Some(&self.0)
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use core::str::FromStr;
116
117    use super::*;
118
119    #[test]
120    fn clone_file_time_range_error() {
121        assert_eq!(
122            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative).clone(),
123            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
124        );
125        assert_eq!(
126            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow).clone(),
127            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
128        );
129    }
130
131    #[test]
132    fn copy_file_time_range_error() {
133        {
134            let a = FileTimeRangeError::new(FileTimeRangeErrorKind::Negative);
135            let b = a;
136            assert_eq!(a, b);
137        }
138
139        {
140            let a = FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow);
141            let b = a;
142            assert_eq!(a, b);
143        }
144    }
145
146    #[test]
147    fn debug_file_time_range_error() {
148        assert_eq!(
149            format!(
150                "{:?}",
151                FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
152            ),
153            "FileTimeRangeError(Negative)"
154        );
155        assert_eq!(
156            format!(
157                "{:?}",
158                FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
159            ),
160            "FileTimeRangeError(Overflow)"
161        );
162    }
163
164    #[test]
165    fn file_time_range_error_equality() {
166        assert_eq!(
167            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative),
168            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
169        );
170        assert_ne!(
171            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative),
172            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
173        );
174        assert_ne!(
175            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow),
176            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
177        );
178        assert_eq!(
179            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow),
180            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
181        );
182    }
183
184    #[test]
185    fn kind_file_time_range_error() {
186        assert_eq!(
187            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative).kind(),
188            FileTimeRangeErrorKind::Negative
189        );
190        assert_eq!(
191            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow).kind(),
192            FileTimeRangeErrorKind::Overflow
193        );
194    }
195
196    #[test]
197    const fn kind_file_time_range_error_is_const_fn() {
198        const _: FileTimeRangeErrorKind =
199            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative).kind();
200    }
201
202    #[test]
203    fn display_file_time_range_error() {
204        assert_eq!(
205            format!(
206                "{}",
207                FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
208            ),
209            "file time is before `1601-01-01 00:00:00 UTC`"
210        );
211        assert_eq!(
212            format!(
213                "{}",
214                FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
215            ),
216            "file time is after `+60056-05-28 05:36:10.955161500 UTC`"
217        );
218    }
219
220    #[test]
221    fn source_file_time_range_error() {
222        assert!(
223            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
224                .source()
225                .is_none()
226        );
227        assert!(
228            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
229                .source()
230                .is_none()
231        );
232    }
233
234    #[test]
235    fn from_file_time_range_error_kind_to_file_time_range_error() {
236        assert_eq!(
237            FileTimeRangeError::from(FileTimeRangeErrorKind::Negative),
238            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
239        );
240        assert_eq!(
241            FileTimeRangeError::from(FileTimeRangeErrorKind::Overflow),
242            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
243        );
244    }
245
246    #[test]
247    fn clone_file_time_range_error_kind() {
248        assert_eq!(
249            FileTimeRangeErrorKind::Negative.clone(),
250            FileTimeRangeErrorKind::Negative
251        );
252        assert_eq!(
253            FileTimeRangeErrorKind::Overflow.clone(),
254            FileTimeRangeErrorKind::Overflow
255        );
256    }
257
258    #[test]
259    fn copy_file_time_range_error_kind() {
260        {
261            let a = FileTimeRangeErrorKind::Negative;
262            let b = a;
263            assert_eq!(a, b);
264        }
265
266        {
267            let a = FileTimeRangeErrorKind::Overflow;
268            let b = a;
269            assert_eq!(a, b);
270        }
271    }
272
273    #[test]
274    fn debug_file_time_range_error_kind() {
275        assert_eq!(
276            format!("{:?}", FileTimeRangeErrorKind::Negative),
277            "Negative"
278        );
279        assert_eq!(
280            format!("{:?}", FileTimeRangeErrorKind::Overflow),
281            "Overflow"
282        );
283    }
284
285    #[test]
286    fn file_time_range_error_kind_equality() {
287        assert_eq!(
288            FileTimeRangeErrorKind::Negative,
289            FileTimeRangeErrorKind::Negative
290        );
291        assert_ne!(
292            FileTimeRangeErrorKind::Negative,
293            FileTimeRangeErrorKind::Overflow
294        );
295        assert_ne!(
296            FileTimeRangeErrorKind::Overflow,
297            FileTimeRangeErrorKind::Negative
298        );
299        assert_eq!(
300            FileTimeRangeErrorKind::Overflow,
301            FileTimeRangeErrorKind::Overflow
302        );
303    }
304
305    #[test]
306    fn display_file_time_range_error_kind() {
307        assert_eq!(
308            format!("{}", FileTimeRangeErrorKind::Negative),
309            "file time is before `1601-01-01 00:00:00 UTC`"
310        );
311        assert_eq!(
312            format!("{}", FileTimeRangeErrorKind::Overflow),
313            "file time is after `+60056-05-28 05:36:10.955161500 UTC`"
314        );
315    }
316
317    #[test]
318    fn debug_parse_file_time_error() {
319        assert_eq!(
320            format!(
321                "{:?}",
322                ParseFileTimeError::new(u64::from_str("").unwrap_err())
323            ),
324            "ParseFileTimeError(ParseIntError { kind: Empty })"
325        );
326        assert_eq!(
327            format!(
328                "{:?}",
329                ParseFileTimeError::new(u64::from_str("a").unwrap_err())
330            ),
331            "ParseFileTimeError(ParseIntError { kind: InvalidDigit })"
332        );
333        assert_eq!(
334            format!(
335                "{:?}",
336                ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
337            ),
338            "ParseFileTimeError(ParseIntError { kind: PosOverflow })"
339        );
340    }
341
342    #[test]
343    fn parse_file_time_error_equality() {
344        assert_eq!(
345            ParseFileTimeError::new(u64::from_str("").unwrap_err()),
346            ParseFileTimeError::new(u64::from_str("").unwrap_err())
347        );
348        assert_ne!(
349            ParseFileTimeError::new(u64::from_str("").unwrap_err()),
350            ParseFileTimeError::new(u64::from_str("a").unwrap_err())
351        );
352        assert_ne!(
353            ParseFileTimeError::new(u64::from_str("").unwrap_err()),
354            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
355        );
356        assert_ne!(
357            ParseFileTimeError::new(u64::from_str("a").unwrap_err()),
358            ParseFileTimeError::new(u64::from_str("").unwrap_err())
359        );
360        assert_eq!(
361            ParseFileTimeError::new(u64::from_str("a").unwrap_err()),
362            ParseFileTimeError::new(u64::from_str("a").unwrap_err())
363        );
364        assert_ne!(
365            ParseFileTimeError::new(u64::from_str("a").unwrap_err()),
366            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
367        );
368        assert_ne!(
369            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err()),
370            ParseFileTimeError::new(u64::from_str("").unwrap_err())
371        );
372        assert_ne!(
373            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err()),
374            ParseFileTimeError::new(u64::from_str("a").unwrap_err())
375        );
376        assert_eq!(
377            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err()),
378            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
379        );
380    }
381
382    #[test]
383    fn display_parse_file_time_error() {
384        assert_eq!(
385            format!(
386                "{}",
387                ParseFileTimeError::new(u64::from_str("").unwrap_err())
388            ),
389            "cannot parse integer from empty string"
390        );
391        assert_eq!(
392            format!(
393                "{}",
394                ParseFileTimeError::new(u64::from_str("a").unwrap_err())
395            ),
396            "invalid digit found in string"
397        );
398        assert_eq!(
399            format!(
400                "{}",
401                ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
402            ),
403            "file time is after `+60056-05-28 05:36:10.955161500 UTC`"
404        );
405    }
406
407    #[test]
408    fn source_parse_file_time_error() {
409        assert_eq!(
410            ParseFileTimeError::new(u64::from_str("").unwrap_err())
411                .source()
412                .unwrap()
413                .downcast_ref::<ParseIntError>()
414                .unwrap()
415                .kind(),
416            &IntErrorKind::Empty
417        );
418        assert_eq!(
419            ParseFileTimeError::new(u64::from_str("a").unwrap_err())
420                .source()
421                .unwrap()
422                .downcast_ref::<ParseIntError>()
423                .unwrap()
424                .kind(),
425            &IntErrorKind::InvalidDigit
426        );
427        assert_eq!(
428            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
429                .source()
430                .unwrap()
431                .downcast_ref::<ParseIntError>()
432                .unwrap()
433                .kind(),
434            &IntErrorKind::PosOverflow
435        );
436    }
437}