openscript 0.1.0

High-performance AI-powered scripting language runtime
Documentation
//! Error types and utilities for OpenScript

use std::fmt;
use thiserror::Error;

/// Result type alias for OpenScript operations
pub type Result<T> = std::result::Result<T, Error>;

/// Comprehensive error type for all OpenScript operations
#[derive(Error, Debug)]
pub enum Error {
    /// Lexical analysis errors
    #[error("Lexer error at line {line}, column {column}: {message}")]
    LexerError {
        /// Line number where the error occurred
        line: usize,
        /// Column number where the error occurred
        column: usize,
        /// Error message
        message: String,
    },

    /// Parser errors
    #[error("Parse error at line {line}: {message}")]
    ParseError {
        /// Line number where the error occurred
        line: usize,
        /// Error message
        message: String,
    },

    /// Runtime execution errors
    #[error("Runtime error: {message}")]
    RuntimeError {
        /// Error message
        message: String,
        /// Optional stack trace
        stack_trace: Option<Vec<String>>,
    },

    /// Variable not found
    #[error("Variable '{name}' is not defined")]
    UndefinedVariable {
        /// Variable name
        name: String,
    },

    /// Function not found
    #[error("Function '{name}' is not defined")]
    UndefinedFunction {
        /// Function name
        name: String,
    },

    /// Type mismatch errors
    #[error("Type error: expected {expected}, got {actual}")]
    TypeError {
        /// Expected type
        expected: String,
        /// Actual type
        actual: String,
    },

    /// Argument count mismatch
    #[error("Argument error: expected {expected} arguments, got {actual}")]
    ArgumentError {
        /// Expected argument count
        expected: usize,
        /// Actual argument count
        actual: usize,
    },

    /// IO errors
    #[error("IO error: {0}")]
    IoError(#[from] std::io::Error),

    /// Network/HTTP errors
    #[error("Network error: {0}")]
    NetworkError(String),

    /// AI service errors
    #[error("AI service error: {0}")]
    AiError(String),

    /// JSON parsing errors
    #[error("JSON error: {0}")]
    JsonError(#[from] serde_json::Error),

    /// Generic errors
    #[error("Internal error: {0}")]
    InternalError(String),
}

impl Error {
    /// Create a new lexer error
    pub fn lexer_error(line: usize, column: usize, message: impl Into<String>) -> Self {
        Self::LexerError {
            line,
            column,
            message: message.into(),
        }
    }

    /// Create a new parse error
    pub fn parse_error(line: usize, message: impl Into<String>) -> Self {
        Self::ParseError {
            line,
            message: message.into(),
        }
    }

    /// Create a new runtime error
    pub fn runtime_error(message: impl Into<String>) -> Self {
        Self::RuntimeError {
            message: message.into(),
            stack_trace: None,
        }
    }

    /// Create a new runtime error with stack trace
    pub fn runtime_error_with_trace(
        message: impl Into<String>,
        stack_trace: Vec<String>,
    ) -> Self {
        Self::RuntimeError {
            message: message.into(),
            stack_trace: Some(stack_trace),
        }
    }

    /// Create a new undefined variable error
    pub fn undefined_variable(name: impl Into<String>) -> Self {
        Self::UndefinedVariable { name: name.into() }
    }

    /// Create a new undefined function error
    pub fn undefined_function(name: impl Into<String>) -> Self {
        Self::UndefinedFunction { name: name.into() }
    }

    /// Create a new type error
    pub fn type_error(expected: impl Into<String>, actual: impl Into<String>) -> Self {
        Self::TypeError {
            expected: expected.into(),
            actual: actual.into(),
        }
    }

    /// Create a new argument error
    pub fn argument_error(expected: usize, actual: usize) -> Self {
        Self::ArgumentError { expected, actual }
    }

    /// Create a new network error
    pub fn network_error(message: impl Into<String>) -> Self {
        Self::NetworkError(message.into())
    }

    /// Create a new AI service error
    pub fn ai_error(message: impl Into<String>) -> Self {
        Self::AiError(message.into())
    }

    /// Create a new internal error
    pub fn internal_error(message: impl Into<String>) -> Self {
        Self::InternalError(message.into())
    }

    /// Check if this is a recoverable error
    pub fn is_recoverable(&self) -> bool {
        matches!(
            self,
            Error::UndefinedVariable { .. }
                | Error::UndefinedFunction { .. }
                | Error::TypeError { .. }
                | Error::ArgumentError { .. }
        )
    }

    /// Get error severity level
    pub fn severity(&self) -> ErrorSeverity {
        match self {
            Error::LexerError { .. } | Error::ParseError { .. } => ErrorSeverity::Fatal,
            Error::RuntimeError { .. } => ErrorSeverity::Error,
            Error::UndefinedVariable { .. }
            | Error::UndefinedFunction { .. }
            | Error::TypeError { .. }
            | Error::ArgumentError { .. } => ErrorSeverity::Error,
            Error::IoError(_) | Error::NetworkError(_) | Error::AiError(_) => ErrorSeverity::Warning,
            Error::JsonError(_) | Error::InternalError(_) => ErrorSeverity::Fatal,
        }
    }
}

/// Error severity levels
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorSeverity {
    /// Fatal errors that prevent execution
    Fatal,
    /// Errors that stop current operation
    Error,
    /// Warnings that don't stop execution
    Warning,
}

impl fmt::Display for ErrorSeverity {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ErrorSeverity::Fatal => write!(f, "FATAL"),
            ErrorSeverity::Error => write!(f, "ERROR"),
            ErrorSeverity::Warning => write!(f, "WARNING"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_error_creation() {
        let err = Error::lexer_error(1, 10, "Unexpected character");
        assert!(matches!(err, Error::LexerError { line: 1, column: 10, .. }));
        assert_eq!(err.severity(), ErrorSeverity::Fatal);
    }

    #[test]
    fn test_error_recoverability() {
        let recoverable = Error::undefined_variable("x");
        assert!(recoverable.is_recoverable());

        let fatal = Error::lexer_error(1, 1, "Invalid token");
        assert!(!fatal.is_recoverable());
    }
}