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#[repr(transparent)]
18#[derive(Copy, Clone, Eq, PartialEq, Hash)]
19pub struct Error(ffi::gpg_error_t);
20
21include!("consts.rs");
22
23impl Error {
24 #[inline]
26 pub const fn new(err: ffi::gpg_error_t) -> Self {
27 Self(err)
28 }
29
30 #[inline]
32 pub const fn raw(&self) -> ffi::gpg_error_t {
33 self.0
34 }
35
36 #[inline]
38 pub fn from_source(source: ErrorSource, code: ErrorCode) -> Self {
39 Error::new(ffi::gpg_err_make(source, code))
40 }
41
42 #[inline]
45 pub fn from_code(code: ErrorCode) -> Self {
46 Error::from_source(Self::SOURCE_UNKNOWN, code)
47 }
48
49 #[inline]
51 pub fn last_os_error() -> Self {
52 unsafe { Error::new(ffi::gpg_error_from_syserror()) }
53 }
54
55 #[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 #[inline]
63 pub fn to_errno(&self) -> i32 {
64 unsafe { ffi::gpg_err_code_to_errno(self.code()) }
65 }
66
67 #[inline]
69 pub const fn code(&self) -> ErrorCode {
70 ffi::gpg_err_code(self.0)
71 }
72
73 #[inline]
75 pub fn source(&self) -> Option<&'static str> {
76 self.raw_source().and_then(|s| str::from_utf8(s).ok())
77 }
78
79 #[inline]
81 pub fn with_source(&self, src: ErrorSource) -> Self {
82 Error::from_source(src, self.code())
83 }
84
85 #[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 #[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 #[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 #[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}