ranty 1.0.0

The Ranty procedural templating language
Documentation
use std::{error::Error, fmt::Display};

use crate::{
    data::DataSourceError, IndexError, KeyError, ModuleResolveError, SelectorError, SliceError,
    ValueError,
};

/// Type alias for `Result<T, RuntimeError>`
pub type RuntimeResult<T> = Result<T, RuntimeError>;
/// A runtime error raised by a Ranty program.
#[derive(Debug)]
pub struct RuntimeError {
    /// The type of runtime error.
    pub error_type: RuntimeErrorType,
    /// A description of what went wrong.
    pub description: Option<String>,
    /// A stack trace describing the location of the error.
    pub stack_trace: Option<String>,
}

impl Error for RuntimeError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match &self.error_type {
            RuntimeErrorType::IndexError(err) => Some(err),
            RuntimeErrorType::KeyError(err) => Some(err),
            RuntimeErrorType::ValueError(err) => Some(err),
            _ => None,
        }
    }

    fn cause(&self) -> Option<&dyn Error> {
        self.source()
    }
}

impl Display for RuntimeError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "[{}] ", self.error_type.id())?;
        if let Some(desc) = &self.description {
            write!(f, "{}", desc)?;
        } else {
            write!(f, "{}", self.error_type)?;
        }
        Ok(())
    }
}

/// Provides general categories of runtime errors encountered in Ranty.
#[derive(Debug)]
pub enum RuntimeErrorType {
    /// Stack has overflowed.
    ///
    /// Ranty error ID: `STACK_OVERFLOW_ERROR`
    StackOverflow,
    /// Stack has underflowed.
    ///
    /// Ranty error ID: `STACK_UNDERFLOW_ERROR`
    StackUnderflow,
    /// Variable access error, such as attempting to access a nonexistent variable or write to a constant
    ///
    /// Ranty error ID: `INVALID_ACCESS_ERROR`
    InvalidAccess,
    /// Operation is not valid for the current program state
    ///
    /// Ranty error ID: `INVALID_OP_ERROR`
    InvalidOperation,
    /// Lazy binding evaluation recursively depended on itself
    ///
    /// Ranty error ID: `LAZY_BINDING_CYCLE_ERROR`
    LazyBindingCycle,
    /// Internal VM error, usually indicating a bug or corrupted data
    ///
    /// Ranty error ID: `INTERNAL_ERROR`
    InternalError,
    /// Too few/many arguments were passed to a function
    ///
    /// Ranty error ID: `ARG_MISMATCH_ERROR`
    ArgumentMismatch,
    /// Invalid argument passed to function
    ///
    /// Ranty error ID: `ARG_ERROR`
    ArgumentError,
    /// Tried to invoke a non-function
    ///
    /// Ranty error ID: `INVOKE_ERROR`
    CannotInvokeValue,
    /// Assertion failed
    ///
    /// Ranty error ID: `ASSERT_ERROR`
    AssertError,
    /// Error occurred due to unexpected value type
    ///
    /// Ranty error ID: `TYPE_ERROR`
    TypeError,
    /// Error occurred when creating value
    ///
    /// Ranty error ID: `VALUE_ERROR`
    ValueError(ValueError),
    /// Error occurred while indexing value
    ///
    /// Ranty error ID: `INDEX_ERROR`
    IndexError(IndexError),
    /// Error occurred while keying value
    ///
    /// Ranty error ID: `KEY_ERROR`
    KeyError(KeyError),
    /// Error occurred while slicing value
    ///
    /// Ranty error ID: `SLICE_ERROR`
    SliceError(SliceError),
    /// Error occurred while iterating selector
    ///
    /// Ranty error ID: `SELECTOR_ERROR`
    SelectorError(SelectorError),
    /// Error occurred while trying to load a module
    ///
    /// Ranty error ID: `MODULE_ERROR`
    ModuleError(ModuleResolveError),
    /// Error manually triggered by program
    ///
    /// Ranty error ID: `USER_ERROR`
    UserError,
    /// Error during control flow operation (e.g. return or break)
    ///
    /// Ranty error ID: `CONTROL_FLOW_ERROR`
    ControlFlowError,
    /// Error occurred during data source operation.
    ///
    /// Ranty error ID: `DATA_SOURCE_ERROR`
    DataSourceError(DataSourceError),
}

impl RuntimeErrorType {
    pub fn id(&self) -> &'static str {
        match self {
            Self::StackOverflow => "STACK_OVERFLOW_ERROR",
            Self::StackUnderflow => "STACK_UNDERFLOW_ERROR",
            Self::InvalidAccess => "INVALID_ACCESS_ERROR",
            Self::InvalidOperation => "INVALID_OP_ERROR",
            Self::LazyBindingCycle => "LAZY_BINDING_CYCLE_ERROR",
            Self::InternalError => "INTERNAL_ERROR",
            Self::ArgumentMismatch => "ARG_MISMATCH_ERROR",
            Self::ArgumentError => "ARG_ERROR",
            Self::CannotInvokeValue => "INVOKE_ERROR",
            Self::UserError => "USER_ERROR",
            Self::AssertError => "ASSERT_ERROR",
            Self::TypeError => "TYPE_ERROR",
            Self::ValueError(_) => "VALUE_ERROR",
            Self::IndexError(_) => "INDEX_ERROR",
            Self::KeyError(_) => "KEY_ERROR",
            Self::SliceError(_) => "SLICE_ERROR",
            Self::SelectorError(_) => "SELECTOR_ERROR",
            Self::ModuleError(_) => "MODULE_ERROR",
            Self::ControlFlowError => "CONTROL_FLOW_ERROR",
            Self::DataSourceError(_) => "DATA_SOURCE_ERROR",
        }
    }
}

impl Display for RuntimeErrorType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::ValueError(e) => write!(f, "{}", e),
            Self::IndexError(e) => write!(f, "{}", e),
            Self::KeyError(e) => write!(f, "{}", e),
            Self::SliceError(e) => write!(f, "{}", e),
            Self::SelectorError(e) => write!(f, "{}", e),
            Self::ModuleError(e) => write!(f, "{}", e),
            Self::DataSourceError(e) => write!(f, "{}", e),
            _ => write!(f, "{}", self.id()),
        }
    }
}

pub(crate) trait IntoRuntimeResult<T> {
    fn into_runtime_result(self) -> RuntimeResult<T>;
}