//! Error handling, reporting and propagating. //! //! Allows us to take advantage of `Result` and interop idiomatic Rust error reporing with the ffi C functions' error reporting. //! Functions returning or constructing `GHOST_TSuccess` will be able to be taken advantage of to be propagated and reported better. //! //! See [`GhostResult`] for more information. use super::*; use std::{ error, fmt, }; /// A result that returns potentially an error. In the form of `GHOST_TSuccess`. /// /// # Usage /// Its function is to provide more ergonomic error handling for Rust code, while still being able to easily interface with the C code. /// # Propagating /// Can be used to use `?` operator on functions returning `GHOST_TSuccess`. /// /// When building on nightly, the following is possible. /// ``` /// # use ghost::{error::GhostResult, c::GHOST_TSuccess}; /// extern "C" {fn returns_tsuccess() -> GHOST_TSuccess;} /// /// fn try_things_internal() -> GHOST_TSuccess /// { /// unsafe { /// returns_tsuccess()?; /// } /// /// GHOST_TSuccess::Success /// } /// /// fn caller() /// { /// match try_things_internal() { /// GHOST_TSuccess::Success => println!("Yay!"), /// GHOST_TSuccess::Failure => panic!("Fug"), /// } /// } /// ``` /// /// On stable, it must be rewritten a bit differently, however: /// /// ``` /// # use ghost::{error::GhostResult, c::GHOST_TSuccess}; /// extern "C" {fn returns_tsuccess() -> GHOST_TSuccess;} /// /// fn try_things_internal() -> GhostResult /// { /// unsafe { /// returns_tsuccess()?; /// } /// /// Ok(()) /// } /// /// fn caller() /// { /// match try_things_internal() { /// Ok(_) => println!("Yay!"), /// Err(_) => panic!("Fug"), /// } /// } /// ``` /// The bottom scenario is preferrable for Rust, as we are using `Result` instead of an integer. /// # When to use /// This type should be preferred in Rust code, and `GHOST_TSuccess` preferred when doing a lot of FFI at once. /// # Notes /// *Usually* the internal result will be `()`. But idk if that will always be the case so it's still possible to set. pub type GhostResult = Result; /// Error type for failed GHOST calls. /// As of yet it doesn't contain any information and is just an opaque zero-sized structure. #[derive(Debug)] pub struct GhostError(pub(crate) ()); impl error::Error for GhostError{} impl fmt::Display for GhostError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "GHOST function call failed") } } impl From for GhostResult { #[inline] fn from(from: types::GHOST_TSuccess) -> Self { from.into_result() } } impl From for types::GHOST_TSuccess { #[inline] fn from(from: GhostResult) -> Self { Self::from_result(from) } } impl fmt::Display for types::GHOST_TSuccess { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Failure => write!(f, "Failure"), Self::Success => write!(f, "Success"), } } } #[cfg(nightly)] impl std::ops::Try for types::GHOST_TSuccess { type Ok = (); type Error = GhostError; fn into_result(self) -> Result { self.into() } fn from_error(_: Self::Error) -> Self { Self::Failure } fn from_ok(_: Self::Ok) -> Self { Self::Success } } impl types::GHOST_TSuccess { /// Consume this instance into `Result<(), GhostError>`. /// pub const fn into_result(self) -> GhostResult { match self { types::GHOST_TSuccess::Success => Ok(()), types::GHOST_TSuccess::Failure => Err(GhostError(())), } } /// Convert a `GhostResult` into `GHOST_TSuccess`. pub const fn from_result(from: GhostResult) -> Self { match from { Ok(_) => Self::Success, Err(_) => Self::Failure, } } }