You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
3.8 KiB

//! Error handling, reporting and propagating.
//!
//! Allows us to take advantage of `Result<T,E>` 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<T,E>` 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<T = ()> = Result<T, GhostError>;
/// 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<types::GHOST_TSuccess> for GhostResult
{
#[inline] fn from(from: types::GHOST_TSuccess) -> Self
{
from.into_result()
}
}
impl From<GhostResult> 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::Ok, Self::Error>
{
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,
}
}
}