use fuel_asm::{Instruction, InstructionResult, PanicReason};
use fuel_tx::ValidationError;
use std::convert::Infallible as StdInfallible;
use std::error::Error as StdError;
use std::{fmt, io};
#[derive(Debug)]
pub enum InterpreterError {
PanicInstruction(InstructionResult),
Panic(PanicReason),
ValidationError(ValidationError),
PredicateFailure,
NoTransactionInitialized,
Io(io::Error),
#[cfg(feature = "debug")]
DebugStateNotInitialized,
}
impl InterpreterError {
pub fn from_runtime(error: RuntimeError, instruction: Instruction) -> Self {
match error {
RuntimeError::Recoverable(reason) => Self::PanicInstruction(InstructionResult::error(reason, instruction)),
RuntimeError::Halt(e) => Self::Io(e),
}
}
pub const fn panic_reason(&self) -> Option<PanicReason> {
match self {
Self::PanicInstruction(result) => Some(*result.reason()),
Self::Panic(reason) => Some(*reason),
_ => None,
}
}
pub const fn instruction(&self) -> Option<&Instruction> {
match self {
Self::PanicInstruction(result) => Some(result.instruction()),
_ => None,
}
}
pub fn instruction_result(&self) -> Option<&InstructionResult> {
match self {
Self::PanicInstruction(r) => Some(r),
_ => None,
}
}
pub fn from_io<E>(e: E) -> Self
where
E: Into<io::Error>,
{
Self::Io(e.into())
}
}
impl From<io::Error> for InterpreterError {
fn from(e: io::Error) -> Self {
InterpreterError::Io(e)
}
}
impl fmt::Display for InterpreterError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ValidationError(e) => {
write!(f, "Failed to validate the transaction: {}", e)
}
Self::Io(e) => {
write!(f, "Unrecoverable error: {}", e)
}
_ => write!(f, "Execution error: {:?}", self),
}
}
}
impl StdError for InterpreterError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Self::ValidationError(e) => Some(e),
Self::Io(e) => Some(e),
_ => None,
}
}
}
impl From<ValidationError> for InterpreterError {
fn from(e: ValidationError) -> Self {
Self::ValidationError(e)
}
}
impl From<InstructionResult> for InterpreterError {
fn from(r: InstructionResult) -> InterpreterError {
Self::PanicInstruction(r)
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde-types-minimal", derive(serde::Serialize, serde::Deserialize))]
pub enum RuntimeError {
Recoverable(PanicReason),
Halt(io::Error),
}
impl RuntimeError {
pub const fn is_recoverable(&self) -> bool {
matches!(self, Self::Recoverable(_))
}
pub const fn must_halt(&self) -> bool {
matches!(self, Self::Halt(_))
}
pub fn from_io<E>(e: E) -> Self
where
E: Into<io::Error>,
{
Self::Halt(e.into())
}
}
impl From<PanicReason> for RuntimeError {
fn from(r: PanicReason) -> Self {
RuntimeError::Recoverable(r)
}
}
impl From<io::Error> for RuntimeError {
fn from(e: io::Error) -> Self {
RuntimeError::Halt(e)
}
}
impl fmt::Display for RuntimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Recoverable(e) => e.fmt(f),
Self::Halt(e) => e.fmt(f),
}
}
}
impl StdError for RuntimeError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Self::Recoverable(e) => Some(e),
Self::Halt(e) => Some(e),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Infallible(StdInfallible);
impl fmt::Display for Infallible {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl StdError for Infallible {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
Some(&self.0)
}
}
impl<E> From<E> for Infallible
where
E: Into<StdInfallible>,
{
fn from(e: E) -> Infallible {
Self(e.into())
}
}
impl From<Infallible> for InterpreterError {
fn from(_e: Infallible) -> InterpreterError {
unreachable!()
}
}
impl From<Infallible> for RuntimeError {
fn from(_e: Infallible) -> RuntimeError {
unreachable!()
}
}
impl From<Infallible> for PanicReason {
fn from(_e: Infallible) -> PanicReason {
unreachable!()
}
}
impl Into<io::Error> for Infallible {
fn into(self) -> io::Error {
unreachable!()
}
}