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 [MS-DOS date and time] was out of range.
14///
15/// [MS-DOS date and time]: https://learn.microsoft.com/en-us/windows/win32/sysinfo/ms-dos-date-and-time
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17#[allow(clippy::module_name_repetitions)]
18pub struct DosDateTimeRangeError(DosDateTimeRangeErrorKind);
19
20impl DosDateTimeRangeError {
21    #[inline]
22    pub(crate) const fn new(kind: DosDateTimeRangeErrorKind) -> Self {
23        Self(kind)
24    }
25
26    /// Returns the corresponding [`DosDateTimeRangeErrorKind`] for this error.
27    ///
28    /// # Examples
29    ///
30    /// ```
31    /// # use nt_time::{FileTime, error::DosDateTimeRangeErrorKind};
32    /// #
33    /// let err = FileTime::NT_TIME_EPOCH.to_dos_date_time(None).unwrap_err();
34    /// assert_eq!(err.kind(), DosDateTimeRangeErrorKind::Negative);
35    ///
36    /// let err = FileTime::MAX.to_dos_date_time(None).unwrap_err();
37    /// assert_eq!(err.kind(), DosDateTimeRangeErrorKind::Overflow);
38    /// ```
39    #[must_use]
40    #[inline]
41    pub const fn kind(&self) -> DosDateTimeRangeErrorKind {
42        self.0
43    }
44}
45
46impl fmt::Display for DosDateTimeRangeError {
47    #[inline]
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        self.kind().fmt(f)
50    }
51}
52
53impl Error for DosDateTimeRangeError {}
54
55impl From<DosDateTimeRangeErrorKind> for DosDateTimeRangeError {
56    #[inline]
57    fn from(kind: DosDateTimeRangeErrorKind) -> Self {
58        Self::new(kind)
59    }
60}
61
62/// Details of the error that caused a [`DosDateTimeRangeError`].
63#[derive(Clone, Copy, Debug, Eq, PartialEq)]
64pub enum DosDateTimeRangeErrorKind {
65    /// Value was negative.
66    ///
67    /// This means the date and time was before "1980-01-01 00:00:00".
68    Negative,
69
70    /// Value was too big to be represented as [MS-DOS date and time].
71    ///
72    /// This means the date and time was after "2107-12-31 23:59:59.990000000".
73    ///
74    /// [MS-DOS date and time]: https://learn.microsoft.com/en-us/windows/win32/sysinfo/ms-dos-date-and-time
75    Overflow,
76}
77
78impl fmt::Display for DosDateTimeRangeErrorKind {
79    #[inline]
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        match self {
82            Self::Negative => {
83                write!(f, "date and time is before `1980-01-01 00:00:00`")
84            }
85            Self::Overflow => {
86                write!(f, "date and time is after `2107-12-31 23:59:59.990000000`")
87            }
88        }
89    }
90}
91
92/// The error type indicating that a [`FileTime`](crate::FileTime) was out of
93/// range.
94#[derive(Clone, Copy, Debug, Eq, PartialEq)]
95#[allow(clippy::module_name_repetitions)]
96pub struct FileTimeRangeError(FileTimeRangeErrorKind);
97
98impl FileTimeRangeError {
99    #[inline]
100    pub(crate) const fn new(kind: FileTimeRangeErrorKind) -> Self {
101        Self(kind)
102    }
103
104    /// Returns the corresponding [`FileTimeRangeErrorKind`] for this error.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// # use nt_time::{FileTime, error::FileTimeRangeErrorKind};
110    /// #
111    /// let err = FileTime::from_unix_time_secs(i64::MIN).unwrap_err();
112    /// assert_eq!(err.kind(), FileTimeRangeErrorKind::Negative);
113    ///
114    /// let err = FileTime::from_unix_time_secs(i64::MAX).unwrap_err();
115    /// assert_eq!(err.kind(), FileTimeRangeErrorKind::Overflow);
116    /// ```
117    #[must_use]
118    #[inline]
119    pub const fn kind(&self) -> FileTimeRangeErrorKind {
120        self.0
121    }
122}
123
124impl fmt::Display for FileTimeRangeError {
125    #[inline]
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        self.kind().fmt(f)
128    }
129}
130
131impl Error for FileTimeRangeError {}
132
133impl From<FileTimeRangeErrorKind> for FileTimeRangeError {
134    #[inline]
135    fn from(kind: FileTimeRangeErrorKind) -> Self {
136        Self::new(kind)
137    }
138}
139
140/// Details of the error that caused a [`FileTimeRangeError`].
141#[derive(Clone, Copy, Debug, Eq, PartialEq)]
142pub enum FileTimeRangeErrorKind {
143    /// Value was negative.
144    ///
145    /// This means the date and time was before "1601-01-01 00:00:00 UTC".
146    Negative,
147
148    /// Value was too big to be represented as [`FileTime`](crate::FileTime).
149    ///
150    /// This means the date and time was after "+60056-05-28 05:36:10.955161500
151    /// UTC".
152    Overflow,
153}
154
155impl fmt::Display for FileTimeRangeErrorKind {
156    #[inline]
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        match self {
159            Self::Negative => {
160                write!(f, "date and time is before `1601-01-01 00:00:00 UTC`")
161            }
162            Self::Overflow => {
163                write!(
164                    f,
165                    "date and time is after `+60056-05-28 05:36:10.955161500 UTC`"
166                )
167            }
168        }
169    }
170}
171
172/// An error which can be returned when parsing a [`FileTime`](crate::FileTime).
173#[derive(Clone, Debug, Eq, PartialEq)]
174#[allow(clippy::module_name_repetitions)]
175pub struct ParseFileTimeError(ParseIntError);
176
177impl ParseFileTimeError {
178    #[inline]
179    pub(crate) const fn new(inner: ParseIntError) -> Self {
180        Self(inner)
181    }
182}
183
184impl fmt::Display for ParseFileTimeError {
185    #[inline]
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        let inner = &self.0;
188        if inner.kind() == &IntErrorKind::PosOverflow {
189            write!(
190                f,
191                "date and time is after `+60056-05-28 05:36:10.955161500 UTC`"
192            )
193        } else {
194            inner.fmt(f)
195        }
196    }
197}
198
199impl Error for ParseFileTimeError {
200    #[inline]
201    fn source(&self) -> Option<&(dyn Error + 'static)> {
202        Some(&self.0)
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use core::str::FromStr;
209
210    use super::*;
211
212    #[test]
213    fn clone_dos_date_time_range_error() {
214        assert_eq!(
215            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative).clone(),
216            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative)
217        );
218        assert_eq!(
219            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow).clone(),
220            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow)
221        );
222    }
223
224    #[test]
225    fn copy_dos_date_time_range_error() {
226        {
227            let a = DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative);
228            let b = a;
229            assert_eq!(a, b);
230        }
231
232        {
233            let a = DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow);
234            let b = a;
235            assert_eq!(a, b);
236        }
237    }
238
239    #[test]
240    fn debug_dos_date_time_range_error() {
241        assert_eq!(
242            format!(
243                "{:?}",
244                DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative)
245            ),
246            "DosDateTimeRangeError(Negative)"
247        );
248        assert_eq!(
249            format!(
250                "{:?}",
251                DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow)
252            ),
253            "DosDateTimeRangeError(Overflow)"
254        );
255    }
256
257    #[test]
258    fn dos_date_time_range_error_equality() {
259        assert_eq!(
260            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative),
261            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative)
262        );
263        assert_ne!(
264            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative),
265            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow)
266        );
267        assert_ne!(
268            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow),
269            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative)
270        );
271        assert_eq!(
272            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow),
273            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow)
274        );
275    }
276
277    #[test]
278    fn kind_dos_date_time_range_error() {
279        assert_eq!(
280            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative).kind(),
281            DosDateTimeRangeErrorKind::Negative
282        );
283        assert_eq!(
284            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow).kind(),
285            DosDateTimeRangeErrorKind::Overflow
286        );
287    }
288
289    #[test]
290    const fn kind_dos_date_time_range_error_is_const_fn() {
291        const _: DosDateTimeRangeErrorKind =
292            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative).kind();
293    }
294
295    #[test]
296    fn display_dos_date_time_range_error() {
297        assert_eq!(
298            format!(
299                "{}",
300                DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative)
301            ),
302            "date and time is before `1980-01-01 00:00:00`"
303        );
304        assert_eq!(
305            format!(
306                "{}",
307                DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow)
308            ),
309            "date and time is after `2107-12-31 23:59:59.990000000`"
310        );
311    }
312
313    #[test]
314    fn source_dos_date_time_range_error() {
315        assert!(
316            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative)
317                .source()
318                .is_none()
319        );
320        assert!(
321            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow)
322                .source()
323                .is_none()
324        );
325    }
326
327    #[test]
328    fn from_dos_date_time_range_error_kind_to_dos_date_time_range_error() {
329        assert_eq!(
330            DosDateTimeRangeError::from(DosDateTimeRangeErrorKind::Negative),
331            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Negative)
332        );
333        assert_eq!(
334            DosDateTimeRangeError::from(DosDateTimeRangeErrorKind::Overflow),
335            DosDateTimeRangeError::new(DosDateTimeRangeErrorKind::Overflow)
336        );
337    }
338
339    #[test]
340    fn clone_dos_date_time_range_error_kind() {
341        assert_eq!(
342            DosDateTimeRangeErrorKind::Negative.clone(),
343            DosDateTimeRangeErrorKind::Negative
344        );
345        assert_eq!(
346            DosDateTimeRangeErrorKind::Overflow.clone(),
347            DosDateTimeRangeErrorKind::Overflow
348        );
349    }
350
351    #[test]
352    fn copy_dos_date_time_range_error_kind() {
353        {
354            let a = DosDateTimeRangeErrorKind::Negative;
355            let b = a;
356            assert_eq!(a, b);
357        }
358
359        {
360            let a = DosDateTimeRangeErrorKind::Overflow;
361            let b = a;
362            assert_eq!(a, b);
363        }
364    }
365
366    #[test]
367    fn debug_dos_date_time_range_error_kind() {
368        assert_eq!(
369            format!("{:?}", DosDateTimeRangeErrorKind::Negative),
370            "Negative"
371        );
372        assert_eq!(
373            format!("{:?}", DosDateTimeRangeErrorKind::Overflow),
374            "Overflow"
375        );
376    }
377
378    #[test]
379    fn dos_date_time_range_error_kind_equality() {
380        assert_eq!(
381            DosDateTimeRangeErrorKind::Negative,
382            DosDateTimeRangeErrorKind::Negative
383        );
384        assert_ne!(
385            DosDateTimeRangeErrorKind::Negative,
386            DosDateTimeRangeErrorKind::Overflow
387        );
388        assert_ne!(
389            DosDateTimeRangeErrorKind::Overflow,
390            DosDateTimeRangeErrorKind::Negative
391        );
392        assert_eq!(
393            DosDateTimeRangeErrorKind::Overflow,
394            DosDateTimeRangeErrorKind::Overflow
395        );
396    }
397
398    #[test]
399    fn display_dos_date_time_range_error_kind() {
400        assert_eq!(
401            format!("{}", DosDateTimeRangeErrorKind::Negative),
402            "date and time is before `1980-01-01 00:00:00`"
403        );
404        assert_eq!(
405            format!("{}", DosDateTimeRangeErrorKind::Overflow),
406            "date and time is after `2107-12-31 23:59:59.990000000`"
407        );
408    }
409
410    #[test]
411    fn clone_file_time_range_error() {
412        assert_eq!(
413            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative).clone(),
414            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
415        );
416        assert_eq!(
417            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow).clone(),
418            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
419        );
420    }
421
422    #[test]
423    fn copy_file_time_range_error() {
424        {
425            let a = FileTimeRangeError::new(FileTimeRangeErrorKind::Negative);
426            let b = a;
427            assert_eq!(a, b);
428        }
429
430        {
431            let a = FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow);
432            let b = a;
433            assert_eq!(a, b);
434        }
435    }
436
437    #[test]
438    fn debug_file_time_range_error() {
439        assert_eq!(
440            format!(
441                "{:?}",
442                FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
443            ),
444            "FileTimeRangeError(Negative)"
445        );
446        assert_eq!(
447            format!(
448                "{:?}",
449                FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
450            ),
451            "FileTimeRangeError(Overflow)"
452        );
453    }
454
455    #[test]
456    fn file_time_range_error_equality() {
457        assert_eq!(
458            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative),
459            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
460        );
461        assert_ne!(
462            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative),
463            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
464        );
465        assert_ne!(
466            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow),
467            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
468        );
469        assert_eq!(
470            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow),
471            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
472        );
473    }
474
475    #[test]
476    fn kind_file_time_range_error() {
477        assert_eq!(
478            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative).kind(),
479            FileTimeRangeErrorKind::Negative
480        );
481        assert_eq!(
482            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow).kind(),
483            FileTimeRangeErrorKind::Overflow
484        );
485    }
486
487    #[test]
488    const fn kind_file_time_range_error_is_const_fn() {
489        const _: FileTimeRangeErrorKind =
490            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative).kind();
491    }
492
493    #[test]
494    fn display_file_time_range_error() {
495        assert_eq!(
496            format!(
497                "{}",
498                FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
499            ),
500            "date and time is before `1601-01-01 00:00:00 UTC`"
501        );
502        assert_eq!(
503            format!(
504                "{}",
505                FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
506            ),
507            "date and time is after `+60056-05-28 05:36:10.955161500 UTC`"
508        );
509    }
510
511    #[test]
512    fn source_file_time_range_error() {
513        assert!(
514            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
515                .source()
516                .is_none()
517        );
518        assert!(
519            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
520                .source()
521                .is_none()
522        );
523    }
524
525    #[test]
526    fn from_file_time_range_error_kind_to_file_time_range_error() {
527        assert_eq!(
528            FileTimeRangeError::from(FileTimeRangeErrorKind::Negative),
529            FileTimeRangeError::new(FileTimeRangeErrorKind::Negative)
530        );
531        assert_eq!(
532            FileTimeRangeError::from(FileTimeRangeErrorKind::Overflow),
533            FileTimeRangeError::new(FileTimeRangeErrorKind::Overflow)
534        );
535    }
536
537    #[test]
538    fn clone_file_time_range_error_kind() {
539        assert_eq!(
540            FileTimeRangeErrorKind::Negative.clone(),
541            FileTimeRangeErrorKind::Negative
542        );
543        assert_eq!(
544            FileTimeRangeErrorKind::Overflow.clone(),
545            FileTimeRangeErrorKind::Overflow
546        );
547    }
548
549    #[test]
550    fn copy_file_time_range_error_kind() {
551        {
552            let a = FileTimeRangeErrorKind::Negative;
553            let b = a;
554            assert_eq!(a, b);
555        }
556
557        {
558            let a = FileTimeRangeErrorKind::Overflow;
559            let b = a;
560            assert_eq!(a, b);
561        }
562    }
563
564    #[test]
565    fn debug_file_time_range_error_kind() {
566        assert_eq!(
567            format!("{:?}", FileTimeRangeErrorKind::Negative),
568            "Negative"
569        );
570        assert_eq!(
571            format!("{:?}", FileTimeRangeErrorKind::Overflow),
572            "Overflow"
573        );
574    }
575
576    #[test]
577    fn file_time_range_error_kind_equality() {
578        assert_eq!(
579            FileTimeRangeErrorKind::Negative,
580            FileTimeRangeErrorKind::Negative
581        );
582        assert_ne!(
583            FileTimeRangeErrorKind::Negative,
584            FileTimeRangeErrorKind::Overflow
585        );
586        assert_ne!(
587            FileTimeRangeErrorKind::Overflow,
588            FileTimeRangeErrorKind::Negative
589        );
590        assert_eq!(
591            FileTimeRangeErrorKind::Overflow,
592            FileTimeRangeErrorKind::Overflow
593        );
594    }
595
596    #[test]
597    fn display_file_time_range_error_kind() {
598        assert_eq!(
599            format!("{}", FileTimeRangeErrorKind::Negative),
600            "date and time is before `1601-01-01 00:00:00 UTC`"
601        );
602        assert_eq!(
603            format!("{}", FileTimeRangeErrorKind::Overflow),
604            "date and time is after `+60056-05-28 05:36:10.955161500 UTC`"
605        );
606    }
607
608    #[test]
609    fn debug_parse_file_time_error() {
610        assert_eq!(
611            format!(
612                "{:?}",
613                ParseFileTimeError::new(u64::from_str("").unwrap_err())
614            ),
615            "ParseFileTimeError(ParseIntError { kind: Empty })"
616        );
617        assert_eq!(
618            format!(
619                "{:?}",
620                ParseFileTimeError::new(u64::from_str("a").unwrap_err())
621            ),
622            "ParseFileTimeError(ParseIntError { kind: InvalidDigit })"
623        );
624        assert_eq!(
625            format!(
626                "{:?}",
627                ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
628            ),
629            "ParseFileTimeError(ParseIntError { kind: PosOverflow })"
630        );
631    }
632
633    #[test]
634    fn parse_file_time_error_equality() {
635        assert_eq!(
636            ParseFileTimeError::new(u64::from_str("").unwrap_err()),
637            ParseFileTimeError::new(u64::from_str("").unwrap_err())
638        );
639        assert_ne!(
640            ParseFileTimeError::new(u64::from_str("").unwrap_err()),
641            ParseFileTimeError::new(u64::from_str("a").unwrap_err())
642        );
643        assert_ne!(
644            ParseFileTimeError::new(u64::from_str("").unwrap_err()),
645            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
646        );
647        assert_ne!(
648            ParseFileTimeError::new(u64::from_str("a").unwrap_err()),
649            ParseFileTimeError::new(u64::from_str("").unwrap_err())
650        );
651        assert_eq!(
652            ParseFileTimeError::new(u64::from_str("a").unwrap_err()),
653            ParseFileTimeError::new(u64::from_str("a").unwrap_err())
654        );
655        assert_ne!(
656            ParseFileTimeError::new(u64::from_str("a").unwrap_err()),
657            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
658        );
659        assert_ne!(
660            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err()),
661            ParseFileTimeError::new(u64::from_str("").unwrap_err())
662        );
663        assert_ne!(
664            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err()),
665            ParseFileTimeError::new(u64::from_str("a").unwrap_err())
666        );
667        assert_eq!(
668            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err()),
669            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
670        );
671    }
672
673    #[test]
674    fn display_parse_file_time_error() {
675        assert_eq!(
676            format!(
677                "{}",
678                ParseFileTimeError::new(u64::from_str("").unwrap_err())
679            ),
680            "cannot parse integer from empty string"
681        );
682        assert_eq!(
683            format!(
684                "{}",
685                ParseFileTimeError::new(u64::from_str("a").unwrap_err())
686            ),
687            "invalid digit found in string"
688        );
689        assert_eq!(
690            format!(
691                "{}",
692                ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
693            ),
694            "date and time is after `+60056-05-28 05:36:10.955161500 UTC`"
695        );
696    }
697
698    #[test]
699    fn source_parse_file_time_error() {
700        assert_eq!(
701            ParseFileTimeError::new(u64::from_str("").unwrap_err())
702                .source()
703                .unwrap()
704                .downcast_ref::<ParseIntError>()
705                .unwrap()
706                .kind(),
707            &IntErrorKind::Empty
708        );
709        assert_eq!(
710            ParseFileTimeError::new(u64::from_str("a").unwrap_err())
711                .source()
712                .unwrap()
713                .downcast_ref::<ParseIntError>()
714                .unwrap()
715                .kind(),
716            &IntErrorKind::InvalidDigit
717        );
718        assert_eq!(
719            ParseFileTimeError::new(u64::from_str("18446744073709551616").unwrap_err())
720                .source()
721                .unwrap()
722                .downcast_ref::<ParseIntError>()
723                .unwrap()
724                .kind(),
725            &IntErrorKind::PosOverflow
726        );
727    }
728}