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