//! Useful for C-interop use super::*; macro_rules! c_try { ($call:expr => $invalid:literal; $fmt:literal $(, $args:expr)*) => { { #[inline(never)] #[cold] fn _panic_bad_c_call<'a>(args: ::std::fmt::Arguments<'a>) -> ! { panic!("C call failed (invalid return {}): {}", $invalid, args) } #[allow(unused_unsafe)] let res = unsafe { $call }; if res == $invalid { _panic_bad_c_call(format_args!($fmt $(, $args)*)) } res } }; ($call:expr => if $func:expr; $fmt:literal $(, $args:expr)*) => { { #[inline(never)] #[cold] fn _panic_bad_c_call<'a, T: ::std::fmt::Display>(invalid: T, args: ::std::fmt::Arguments<'a>) -> ! { panic!("C call failed (invalid return {}): {}", invalid, args) } #[allow(unused_unsafe)] let res = unsafe { $call }; if $func(res) { _panic_bad_c_call(res, format_args!($fmt $(, $args)*)); } res } }; (? $call:expr => if $func:expr; $fmt:literal $(, $args:expr)*) => { { #[allow(unused_unsafe)] let res = unsafe { $call }; if $func(res) { Err(FFIError::from_last_error(res, format_args!($fmt $(, $args)*))) } else { Ok(res) } } }; (? $call:expr => $invalid:literal; $fmt:literal $(, $args:expr)*) => { { #[allow(unused_unsafe)] let res = unsafe { $call }; if res == $invalid { Err(FFIError::from_last_error($invalid, format_args!($fmt $(, $args)*))) } else { Ok(res) } } }; /* Eh... Idk why this doesn't work... ($call:expr => {$($invalid:pat $(if $pred:pat)?),+} => $fmt:literal $(, $args:expr)*) => { { #[inline(never)] #[cold] fn _panic_bad_c_call<'a, T: ::std::fmt::Display>(invalid: T, args: ::std::fmt::Arguments<'a>) -> ! { panic!("C call failed (invalid return {}): {}", invalid, args) } let res = $call; match res { $($invalid $(if $pred)? => _panic_bad_c_call(res, format_args!($fmt $(, $args)*))),* x => x, } } };*/ } pub(crate) use c_try; /// Error context for a failed C call. /// Returns the invalid return value, the `errno` error, and a message. #[derive(Debug)] pub struct FFIError<'a, T>(T, io::Error, fmt::Arguments<'a>); impl<'a, T> FFIError<'a, T> where FFIError<'a, T>: error::Error { #[inline(never)] #[cold] fn from_last_error(value: T, arguments: fmt::Arguments<'a>) -> Self { Self(value, io::Error::last_os_error(), arguments) } } impl<'a, T> AsRef for FFIError<'a, T> { #[inline] fn as_ref(&self) -> &io::Error { &self.1 } } impl<'a, T> FFIError<'a, T> { /// A reference to the value #[inline] pub fn value(&self) -> &T { &self.0 } /// Clone an instance of the value #[inline] pub fn to_value(&self) -> T where T: Clone { self.0.clone() } /// Consume into the value #[inline] pub fn into_value(self) -> T { self.0 } /// Consume into a recursive 2-tuple of `((value, error), message)`. #[inline] pub fn into_parts(self) -> ((T, io::Error), impl fmt::Display + fmt::Debug + 'a) { ((self.0, self.1), self.2) } /// A reference to the inner OS error #[inline] pub fn error(&self) -> &io::Error { &self.1 } /// Get a reference to an opaque type that can be formatted into the message #[inline] pub fn message(&self) -> &(impl fmt::Display + fmt::Debug + 'a) { &self.2 } /// Consume an opaque type that can be formatted into the message pub fn into_message(self) -> impl fmt::Display + fmt::Debug + 'a { self.2 } /* This doesn't work... /// Render any referenced arguments in the message into a string, reducing the lifetime requirement of the message to `'static`. /// /// # Notes /// If `T` is not also `'static`, then the resulting instance will not be `'static` itself. If `T` is not `'static`, use `into_owned()` instead. #[inline] pub fn message_into_owned(self) -> FFIError<'static, T> { FFIError(self.0, self.1, format_args!("{}", self.2.to_string())) } /// Clone any referenced arguments of the message and the value into a non-referential object, reducing the lifetime requirements of the returned instance to `'static`. #[inline] pub fn into_owned(self) -> FFIError<'static, T::Owned> where T: ToOwned, T::Owned: 'static { FFIError(self.0.to_owned(), self.1, format_args!("{}", self.2.to_string())) } */ } impl<'a, T> error::Error for FFIError<'a, T> where FFIError<'a, T>: fmt::Display + fmt::Debug { #[inline] fn source(&self) -> Option<&(dyn error::Error + 'static)> { Some(&self.1) } } impl<'a, T: fmt::Debug> fmt::Display for FFIError<'a, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "C call failed (invalid return {:?}): {}", self.0, &self.2) } } impl<'a, T> From> for io::Error { #[inline] fn from(from: FFIError<'a, T>) -> Self { from.1 } }