gpg_error/
lib.rs

1use std::{
2    borrow::Cow,
3    convert::Infallible,
4    error,
5    ffi::{CStr, NulError},
6    fmt::{self, Write},
7    io::{self, ErrorKind},
8    num::TryFromIntError,
9    os::raw::c_int,
10    result, str,
11};
12
13pub type ErrorSource = ffi::gpg_err_source_t;
14pub type ErrorCode = ffi::gpg_err_code_t;
15
16/// A type wrapping errors produced by GPG libraries.
17#[repr(transparent)]
18#[derive(Copy, Clone, Eq, PartialEq, Hash)]
19pub struct Error(ffi::gpg_error_t);
20
21include!("consts.rs");
22
23impl Error {
24    /// Creates a new error from a raw error value.
25    #[inline]
26    pub const fn new(err: ffi::gpg_error_t) -> Self {
27        Self(err)
28    }
29
30    /// Returns the raw error value that this error wraps.
31    #[inline]
32    pub const fn raw(&self) -> ffi::gpg_error_t {
33        self.0
34    }
35
36    /// Creates a new error from an error source and an error code.
37    #[inline]
38    pub fn from_source(source: ErrorSource, code: ErrorCode) -> Self {
39        Error::new(ffi::gpg_err_make(source, code))
40    }
41
42    /// Creates a new error from an error code using the default
43    /// error source `SOURCE_UNKNOWN`.
44    #[inline]
45    pub fn from_code(code: ErrorCode) -> Self {
46        Error::from_source(Self::SOURCE_UNKNOWN, code)
47    }
48
49    /// Returns an error representing the last OS error that occurred.
50    #[inline]
51    pub fn last_os_error() -> Self {
52        unsafe { Error::new(ffi::gpg_error_from_syserror()) }
53    }
54
55    /// Creates a new error from an OS error code.
56    #[inline]
57    pub fn from_errno(code: i32) -> Self {
58        unsafe { Error::new(ffi::gpg_error_from_errno(code as c_int)) }
59    }
60
61    /// Returns the OS error that this error represents.
62    #[inline]
63    pub fn to_errno(&self) -> i32 {
64        unsafe { ffi::gpg_err_code_to_errno(self.code()) }
65    }
66
67    /// Returns the error code.
68    #[inline]
69    pub const fn code(&self) -> ErrorCode {
70        ffi::gpg_err_code(self.0)
71    }
72
73    /// Returns a description of the source of the error as a UTF-8 string.
74    #[inline]
75    pub fn source(&self) -> Option<&'static str> {
76        self.raw_source().and_then(|s| str::from_utf8(s).ok())
77    }
78
79    /// Returns an `Error` with the same code from the provided source.
80    #[inline]
81    pub fn with_source(&self, src: ErrorSource) -> Self {
82        Error::from_source(src, self.code())
83    }
84
85    /// Returns a description of the source of the error as a slice of bytes.
86    #[inline]
87    pub fn raw_source(&self) -> Option<&'static [u8]> {
88        unsafe {
89            ffi::gpg_strsource(self.0)
90                .as_ref()
91                .map(|s| CStr::from_ptr(s).to_bytes())
92        }
93    }
94
95    /// Returns a printable description of the error.
96    #[inline]
97    pub fn description(&self) -> Cow<'static, str> {
98        let mut buf = [0; 1024];
99        match self.write_description(&mut buf) {
100            Ok(b) => Cow::Owned(String::from_utf8_lossy(b).into_owned()),
101            Err(_) => Cow::Borrowed("Unknown error"),
102        }
103    }
104
105    /// Returns a description of the error as a slice of bytes.
106    #[inline]
107    pub fn raw_description(&self) -> Cow<'static, [u8]> {
108        let mut buf = [0; 1024];
109        match self.write_description(&mut buf) {
110            Ok(b) => Cow::Owned(b.to_owned()),
111            Err(_) => Cow::Borrowed(b"Unknown error"),
112        }
113    }
114
115    /// Writes a description of the error to the provided buffer
116    /// and returns a slice of the buffer containing the description.
117    ///
118    /// # Errors
119    ///
120    /// Returns an error if the provided buffer is not long enough or
121    /// if the error is not recognized.
122    #[inline]
123    pub fn write_description<'r>(&self, buf: &'r mut [u8]) -> result::Result<&'r mut [u8], ()> {
124        let p = buf.as_mut_ptr();
125        unsafe {
126            if ffi::gpg_strerror_r(self.0, p as *mut _, buf.len()) == 0 {
127                match buf.iter().position(|&b| b == b'\0') {
128                    Some(x) => Ok(&mut buf[..x]),
129                    None => Ok(buf),
130                }
131            } else {
132                Err(())
133            }
134        }
135    }
136}
137
138impl From<ffi::gpg_error_t> for Error {
139    #[inline]
140    fn from(e: ffi::gpg_error_t) -> Self {
141        Self::new(e)
142    }
143}
144
145impl error::Error for Error {
146    #[inline]
147    fn description(&self) -> &str {
148        "gpg error"
149    }
150}
151
152struct Escaped<'a>(&'a [u8]);
153impl fmt::Debug for Escaped<'_> {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        f.write_char('"')?;
156        for b in self.0.iter().flat_map(|&b| b.escape_ascii()) {
157            f.write_char(b as char)?;
158        }
159        f.write_char('"')
160    }
161}
162
163impl fmt::Display for Escaped<'_> {
164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165        let mut buf = self.0;
166        loop {
167            match str::from_utf8(buf) {
168                Ok(s) => {
169                    f.write_str(s)?;
170                    break;
171                }
172                Err(e) => {
173                    let (valid, broken) = buf.split_at(e.valid_up_to());
174                    f.write_str(unsafe { str::from_utf8_unchecked(valid) })?;
175                    f.write_char(char::REPLACEMENT_CHARACTER)?;
176                    match e.error_len() {
177                        Some(l) => buf = &broken[l..],
178                        None => break,
179                    }
180                }
181            }
182        }
183        Ok(())
184    }
185}
186
187impl fmt::Debug for Error {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        let mut buf = [0; 1024];
190        let desc = self
191            .write_description(&mut buf)
192            .map(|x| &*x)
193            .unwrap_or(b"Unknown error");
194        f.debug_struct("Error")
195            .field("source", &self.source())
196            .field("code", &self.code())
197            .field("description", &Escaped(desc))
198            .finish()
199    }
200}
201
202impl fmt::Display for Error {
203    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
204        let mut buf = [0; 1024];
205        let desc = self
206            .write_description(&mut buf)
207            .map(|x| &*x)
208            .unwrap_or(b"Unknown error");
209        write!(fmt, "{} (gpg error {})", Escaped(desc), self.code())
210    }
211}
212
213impl From<Infallible> for Error {
214    #[inline]
215    fn from(x: Infallible) -> Self {
216        match x {}
217    }
218}
219
220impl From<NulError> for Error {
221    #[inline]
222    fn from(_: NulError) -> Self {
223        Self::EINVAL
224    }
225}
226
227impl From<TryFromIntError> for Error {
228    #[inline]
229    fn from(_: TryFromIntError) -> Self {
230        Self::EINVAL
231    }
232}
233
234impl From<io::Error> for Error {
235    fn from(err: io::Error) -> Self {
236        let kind = err.kind();
237        if let Some(Ok(err)) = err.into_inner().map(|e| e.downcast::<Self>()) {
238            *err
239        } else {
240            match kind {
241                ErrorKind::AddrInUse => Self::EADDRINUSE,
242                ErrorKind::AddrNotAvailable => Self::EADDRNOTAVAIL,
243                ErrorKind::AlreadyExists => Self::EEXIST,
244                ErrorKind::BrokenPipe => Self::EPIPE,
245                ErrorKind::ConnectionAborted => Self::ECONNABORTED,
246                ErrorKind::ConnectionRefused => Self::ECONNREFUSED,
247                ErrorKind::ConnectionReset => Self::ECONNRESET,
248                ErrorKind::Interrupted => Self::EINTR,
249                ErrorKind::InvalidInput => Self::EINVAL,
250                ErrorKind::NotConnected => Self::ENOTCONN,
251                ErrorKind::NotFound => Self::ENOENT,
252                ErrorKind::OutOfMemory => Self::ENOMEM,
253                ErrorKind::PermissionDenied => Self::EACCES,
254                ErrorKind::TimedOut => Self::ETIMEDOUT,
255                ErrorKind::Unsupported => Self::ENOSYS,
256                ErrorKind::WouldBlock => Self::EWOULDBLOCK,
257                _ => Error::EIO,
258            }
259        }
260    }
261}
262
263impl From<Error> for io::Error {
264    fn from(err: Error) -> Self {
265        let kind = match err.with_source(Error::SOURCE_UNKNOWN) {
266            Error::EADDRINUSE => ErrorKind::AddrInUse,
267            Error::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
268            Error::ECONNABORTED => ErrorKind::ConnectionAborted,
269            Error::ECONNREFUSED => ErrorKind::ConnectionRefused,
270            Error::ECONNRESET => ErrorKind::ConnectionReset,
271            Error::EEXIST | Error::LDAP_ALREADY_EXISTS => ErrorKind::AlreadyExists,
272            Error::EINTR | Error::SQL_INTERRUPT => ErrorKind::Interrupted,
273            Error::EINVAL | Error::EDOM => ErrorKind::InvalidInput,
274            Error::ENOENT
275            | Error::ENODEV
276            | Error::ENXIO
277            | Error::ESRCH
278            | Error::LDAP_NO_RESULTS
279            | Error::SQL_NOTFOUND => ErrorKind::NotFound,
280            Error::ENOMEM | Error::LDAP_NO_MEMORY | Error::SQL_NOMEM => ErrorKind::OutOfMemory,
281            Error::ENOSYS
282            | Error::ENOTSUP
283            | Error::EOPNOTSUPP
284            | Error::EAFNOSUPPORT
285            | Error::EPROTONOSUPPORT
286            | Error::NOT_SUPPORTED
287            | Error::LDAP_NOT_SUPPORTED => ErrorKind::Unsupported,
288            Error::ENOTCONN => ErrorKind::NotConnected,
289            Error::EACCES | Error::EPERM | Error::SQL_PERM => ErrorKind::PermissionDenied,
290            Error::EPIPE => ErrorKind::BrokenPipe,
291            Error::ETIMEDOUT | Error::TIMEOUT | Error::DNS_TIMEOUT | Error::LDAP_TIMEOUT => {
292                ErrorKind::TimedOut
293            }
294            x if x == Error::EAGAIN || x == Error::EWOULDBLOCK => ErrorKind::WouldBlock,
295            _ => ErrorKind::Other,
296        };
297        Self::new(kind, err)
298    }
299}
300
301pub type Result<T, E = Error> = result::Result<T, E>;
302
303#[macro_export]
304macro_rules! return_err {
305    ($e:expr) => {
306        match $crate::Error::from($e) {
307            $crate::Error::NO_ERROR => (),
308            err => return Err(From::from(err)),
309        }
310    };
311}
312
313#[cfg(test)]
314mod tests {
315    use super::Error;
316
317    #[test]
318    fn test_errno() {
319        let e = Error::from_errno(0);
320        assert_eq!(e.to_errno(), 0);
321        assert_eq!(e.code(), 0);
322        assert_eq!(e, Error::NO_ERROR);
323    }
324
325    #[test]
326    fn test_syserror() {
327        unsafe {
328            ffi::gpg_err_set_errno(0);
329        }
330        let e = Error::last_os_error();
331        assert_eq!(e.to_errno(), 0);
332        assert_eq!(e, Error::MISSING_ERRNO);
333    }
334}