From 81e142c6b72b45d0ff285b60762c8a1ef200d6c1 Mon Sep 17 00:00:00 2001 From: Zhiming Wang Date: Sat, 8 Aug 2020 23:16:09 +0800 Subject: [PATCH] util: introduce new Error variant Error:Other { errno } Do not hide all the POSIX error codes behind a useless Error::Unknown. Fixes #24. --- src/util/error.rs | 64 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/src/util/error.rs b/src/util/error.rs index d0509d6..a6740e9 100644 --- a/src/util/error.rs +++ b/src/util/error.rs @@ -7,7 +7,21 @@ use std::str::from_utf8_unchecked; use ffi::*; use libc::{c_char, c_int}; -#[derive(Copy, Clone)] +// Export POSIX error codes so that users can do something like `if error == +// Error::Other { errno: EAGAIN }`. +pub use libc::{ + E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, EAFNOSUPPORT, EAGAIN, EALREADY, EBADF, EBADMSG, + EBUSY, ECANCELED, ECHILD, ECONNABORTED, ECONNREFUSED, ECONNRESET, EDEADLK, EDESTADDRREQ, EDOM, + EDQUOT, EEXIST, EFAULT, EFBIG, EHOSTUNREACH, EIDRM, EILSEQ, EINPROGRESS, EINTR, EINVAL, EIO, + EISCONN, EISDIR, ELOOP, EMFILE, EMLINK, EMSGSIZE, EMULTIHOP, ENAMETOOLONG, ENETDOWN, ENETRESET, + ENETUNREACH, ENFILE, ENOBUFS, ENODATA, ENODEV, ENOENT, ENOEXEC, ENOLCK, ENOLINK, ENOMEM, + ENOMSG, ENOPROTOOPT, ENOSPC, ENOSR, ENOSTR, ENOSYS, ENOTCONN, ENOTDIR, ENOTEMPTY, + ENOTRECOVERABLE, ENOTSOCK, ENOTSUP, ENOTTY, ENXIO, EOPNOTSUPP, EOVERFLOW, EOWNERDEAD, EPERM, + EPIPE, EPROTO, EPROTONOSUPPORT, EPROTOTYPE, ERANGE, EROFS, ESPIPE, ESRCH, ESTALE, ETIME, + ETIMEDOUT, ETXTBSY, EWOULDBLOCK, EXDEV, +}; + +#[derive(Copy, Clone, PartialEq)] pub enum Error { Bug, Bug2, @@ -39,6 +53,11 @@ pub enum Error { HttpNotFound, HttpOther4xx, HttpServerError, + + /// For AVERROR(e) wrapping POSIX error codes, e.g. AVERROR(EAGAIN). + Other { + errno: c_int, + }, } impl From for Error { @@ -71,8 +90,9 @@ impl From for Error { AVERROR_HTTP_NOT_FOUND => Error::HttpNotFound, AVERROR_HTTP_OTHER_4XX => Error::HttpOther4xx, AVERROR_HTTP_SERVER_ERROR => Error::HttpServerError, - - _ => Error::Unknown, + e => Error::Other { + errno: AVUNERROR(e), + }, } } } @@ -107,6 +127,7 @@ impl Into for Error { Error::HttpNotFound => AVERROR_HTTP_NOT_FOUND, Error::HttpOther4xx => AVERROR_HTTP_OTHER_4XX, Error::HttpServerError => AVERROR_HTTP_SERVER_ERROR, + Error::Other { errno } => AVERROR(errno), } } } @@ -122,7 +143,13 @@ impl From for io::Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { f.write_str(unsafe { - from_utf8_unchecked(CStr::from_ptr(STRINGS[index(self)].as_ptr()).to_bytes()) + from_utf8_unchecked( + CStr::from_ptr(match *self { + Error::Other { errno } => libc::strerror(errno), + _ => STRINGS[index(self)].as_ptr(), + }) + .to_bytes(), + ) }) } } @@ -166,12 +193,13 @@ fn index(error: &Error) -> usize { Error::HttpNotFound => 24, Error::HttpOther4xx => 25, Error::HttpServerError => 26, + Error::Other { errno: _ } => (-1isize) as usize, } } // XXX: the length has to be synced with the number of errors -static mut STRINGS: [[c_char; AV_ERROR_MAX_STRING_SIZE as usize]; 27] = - [[0 as c_char; AV_ERROR_MAX_STRING_SIZE as usize]; 27]; +static mut STRINGS: [[c_char; AV_ERROR_MAX_STRING_SIZE]; 27] = + [[0 as c_char; AV_ERROR_MAX_STRING_SIZE]; 27]; pub fn register_all() { unsafe { @@ -315,3 +343,27 @@ pub fn register_all() { ); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_roundtrip() { + assert_eq!(Into::::into(Error::from(AVERROR_EOF)), AVERROR_EOF); + assert_eq!( + Into::::into(Error::from(AVERROR(EAGAIN))), + AVERROR(EAGAIN) + ); + assert_eq!(Error::from(AVERROR(EAGAIN)), Error::Other { errno: EAGAIN }); + } + + #[cfg(any(target_os = "linux", target_os = "macos"))] + #[test] + fn test_posix_error_string() { + assert_eq!( + Error::from(AVERROR(EAGAIN)).to_string(), + "Resource temporarily unavailable" + ) + } +}