zip/
result.rs

1#![allow(unknown_lints)] // non_local_definitions isn't in Rust 1.70
2#![allow(non_local_definitions)]
3//! Error types that can be emitted from this library
4
5use std::borrow::Cow;
6use std::error::Error;
7use std::fmt::{self, Display, Formatter};
8use std::io;
9use std::num::TryFromIntError;
10use std::string::FromUtf8Error;
11
12/// Generic result type with ZipError as its error variant
13pub type ZipResult<T> = Result<T, ZipError>;
14
15/// Error type for Zip
16#[derive(Debug)]
17#[non_exhaustive]
18pub enum ZipError {
19    /// i/o error
20    Io(io::Error),
21
22    /// invalid Zip archive
23    InvalidArchive(Cow<'static, str>),
24
25    /// unsupported Zip archive
26    UnsupportedArchive(&'static str),
27
28    /// specified file not found in archive
29    FileNotFound,
30
31    /// provided password is incorrect
32    InvalidPassword,
33}
34
35impl ZipError {
36    /// The text used as an error when a password is required and not supplied
37    ///
38    /// ```rust,no_run
39    /// # use zip::result::ZipError;
40    /// # let mut archive = zip::ZipArchive::new(std::io::Cursor::new(&[])).unwrap();
41    /// match archive.by_index(1) {
42    ///     Err(ZipError::UnsupportedArchive(ZipError::PASSWORD_REQUIRED)) => eprintln!("a password is needed to unzip this file"),
43    ///     _ => (),
44    /// }
45    /// # ()
46    /// ```
47    pub const PASSWORD_REQUIRED: &'static str = "Password required to decrypt file";
48}
49
50impl Display for ZipError {
51    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
52        match self {
53            Self::Io(_) => f.write_str("i/o error"),
54            Self::InvalidArchive(e) => write!(f, "invalid Zip archive: {}", e),
55            Self::UnsupportedArchive(e) => write!(f, "unsupported Zip archive: {}", e),
56            Self::FileNotFound => f.write_str("specified file not found in archive"),
57            Self::InvalidPassword => f.write_str("provided password is incorrect"),
58        }
59    }
60}
61
62impl Error for ZipError {
63    fn source(&self) -> Option<&(dyn Error + 'static)> {
64        match self {
65            Self::Io(e) => Some(e),
66            Self::InvalidArchive(_)
67            | Self::UnsupportedArchive(_)
68            | Self::FileNotFound
69            | Self::InvalidPassword => None,
70        }
71    }
72}
73
74impl From<ZipError> for io::Error {
75    fn from(err: ZipError) -> io::Error {
76        let kind = match &err {
77            ZipError::Io(err) => err.kind(),
78            ZipError::InvalidArchive(_) => io::ErrorKind::InvalidData,
79            ZipError::UnsupportedArchive(_) => io::ErrorKind::Unsupported,
80            ZipError::FileNotFound => io::ErrorKind::NotFound,
81            ZipError::InvalidPassword => io::ErrorKind::InvalidInput,
82        };
83
84        io::Error::new(kind, err)
85    }
86}
87
88impl From<io::Error> for ZipError {
89    fn from(value: io::Error) -> Self {
90        Self::Io(value)
91    }
92}
93
94impl From<DateTimeRangeError> for ZipError {
95    fn from(_: DateTimeRangeError) -> Self {
96        invalid!("Invalid date or time")
97    }
98}
99
100impl From<FromUtf8Error> for ZipError {
101    fn from(_: FromUtf8Error) -> Self {
102        invalid!("Invalid UTF-8")
103    }
104}
105
106/// Error type for time parsing
107#[derive(Debug)]
108pub struct DateTimeRangeError;
109
110// TryFromIntError is also an out-of-range error.
111impl From<TryFromIntError> for DateTimeRangeError {
112    fn from(_value: TryFromIntError) -> Self {
113        DateTimeRangeError
114    }
115}
116
117impl fmt::Display for DateTimeRangeError {
118    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
119        write!(
120            fmt,
121            "a date could not be represented within the bounds the MS-DOS date range (1980-2107)"
122        )
123    }
124}
125
126impl Error for DateTimeRangeError {}
127
128pub(crate) fn invalid_archive<M: Into<Cow<'static, str>>>(message: M) -> ZipError {
129    ZipError::InvalidArchive(message.into())
130}
131
132pub(crate) const fn invalid_archive_const(message: &'static str) -> ZipError {
133    ZipError::InvalidArchive(Cow::Borrowed(message))
134}
135
136macro_rules! invalid {
137    ($message:literal) => {
138        crate::result::invalid_archive_const($message)
139    };
140    ($($arg:tt)*) => {
141        crate::result::invalid_archive(format!($($arg)*))
142    };
143}
144pub(crate) use invalid;