typhoon-errors 0.3.0

Error handling types and utilities
Documentation
#![no_std]

mod error_code;
mod extension;

pub use {error_code::*, extension::*};
use {
    solana_address::error::AddressError,
    solana_program_error::{ProgramError, ToStr},
};

pub struct Error {
    error: ProgramError,
    account_name: Option<&'static str>,
}

impl Error {
    pub fn new(error: impl Into<ProgramError>) -> Self {
        Error {
            error: error.into(),
            account_name: None,
        }
    }

    pub fn with_account(mut self, name: &'static str) -> Self {
        self.account_name = Some(name);
        self
    }

    pub fn account_name(&self) -> Option<&str> {
        self.account_name
    }

    pub fn to_str<E>(&self) -> &'static str
    where
        E: ToStr + TryFrom<u32> + 'static,
    {
        if let ProgramError::Custom(code) = self.error {
            if (100..200).contains(&code) {
                return self.error.to_str::<ErrorCode>();
            }
        }
        self.error.to_str::<E>()
    }
}

impl From<ProgramError> for Error {
    fn from(error: ProgramError) -> Self {
        Error {
            error,
            account_name: None,
        }
    }
}

impl From<ErrorCode> for Error {
    fn from(value: ErrorCode) -> Self {
        Error {
            error: value.into(),
            account_name: None,
        }
    }
}

impl From<Error> for ProgramError {
    fn from(value: Error) -> Self {
        value.error
    }
}

impl From<AddressError> for Error {
    fn from(value: AddressError) -> Self {
        Self {
            error: value.into(),
            account_name: None,
        }
    }
}

#[cfg(feature = "logging")]
pub type LogError = ErrorCode;

#[cfg(feature = "logging")]
#[cold]
pub fn log_error<E>(error: &Error)
where
    E: ToStr + TryFrom<u32> + 'static,
{
    solana_program_log::log(error.to_str::<E>());

    if let Some(account_name) = error.account_name() {
        let mut logger = solana_program_log::Logger::<50>::default();
        logger.append("Account origin: ");
        logger.append(unsafe { str::from_utf8_unchecked(account_name.as_bytes()) });
        logger.log();
    }
}

#[macro_export]
macro_rules! require {
    ( $constraint:expr, $error:expr ) => {
        if pinocchio::hint::unlikely(!$constraint) {
            return Err($error.into());
        }
    };
}