openscript/
error.rs

1//! Error types and utilities for OpenScript
2
3use std::fmt;
4use thiserror::Error;
5
6/// Result type alias for OpenScript operations
7pub type Result<T> = std::result::Result<T, Error>;
8
9/// Comprehensive error type for all OpenScript operations
10#[derive(Error, Debug)]
11pub enum Error {
12    /// Lexical analysis errors
13    #[error("Lexer error at line {line}, column {column}: {message}")]
14    LexerError {
15        /// Line number where the error occurred
16        line: usize,
17        /// Column number where the error occurred
18        column: usize,
19        /// Error message
20        message: String,
21    },
22
23    /// Parser errors
24    #[error("Parse error at line {line}: {message}")]
25    ParseError {
26        /// Line number where the error occurred
27        line: usize,
28        /// Error message
29        message: String,
30    },
31
32    /// Runtime execution errors
33    #[error("Runtime error: {message}")]
34    RuntimeError {
35        /// Error message
36        message: String,
37        /// Optional stack trace
38        stack_trace: Option<Vec<String>>,
39    },
40
41    /// Variable not found
42    #[error("Variable '{name}' is not defined")]
43    UndefinedVariable {
44        /// Variable name
45        name: String,
46    },
47
48    /// Function not found
49    #[error("Function '{name}' is not defined")]
50    UndefinedFunction {
51        /// Function name
52        name: String,
53    },
54
55    /// Type mismatch errors
56    #[error("Type error: expected {expected}, got {actual}")]
57    TypeError {
58        /// Expected type
59        expected: String,
60        /// Actual type
61        actual: String,
62    },
63
64    /// Argument count mismatch
65    #[error("Argument error: expected {expected} arguments, got {actual}")]
66    ArgumentError {
67        /// Expected argument count
68        expected: usize,
69        /// Actual argument count
70        actual: usize,
71    },
72
73    /// IO errors
74    #[error("IO error: {0}")]
75    IoError(#[from] std::io::Error),
76
77    /// Network/HTTP errors
78    #[error("Network error: {0}")]
79    NetworkError(String),
80
81    /// AI service errors
82    #[error("AI service error: {0}")]
83    AiError(String),
84
85    /// JSON parsing errors
86    #[error("JSON error: {0}")]
87    JsonError(#[from] serde_json::Error),
88
89    /// Generic errors
90    #[error("Internal error: {0}")]
91    InternalError(String),
92}
93
94impl Error {
95    /// Create a new lexer error
96    pub fn lexer_error(line: usize, column: usize, message: impl Into<String>) -> Self {
97        Self::LexerError {
98            line,
99            column,
100            message: message.into(),
101        }
102    }
103
104    /// Create a new parse error
105    pub fn parse_error(line: usize, message: impl Into<String>) -> Self {
106        Self::ParseError {
107            line,
108            message: message.into(),
109        }
110    }
111
112    /// Create a new runtime error
113    pub fn runtime_error(message: impl Into<String>) -> Self {
114        Self::RuntimeError {
115            message: message.into(),
116            stack_trace: None,
117        }
118    }
119
120    /// Create a new runtime error with stack trace
121    pub fn runtime_error_with_trace(
122        message: impl Into<String>,
123        stack_trace: Vec<String>,
124    ) -> Self {
125        Self::RuntimeError {
126            message: message.into(),
127            stack_trace: Some(stack_trace),
128        }
129    }
130
131    /// Create a new undefined variable error
132    pub fn undefined_variable(name: impl Into<String>) -> Self {
133        Self::UndefinedVariable { name: name.into() }
134    }
135
136    /// Create a new undefined function error
137    pub fn undefined_function(name: impl Into<String>) -> Self {
138        Self::UndefinedFunction { name: name.into() }
139    }
140
141    /// Create a new type error
142    pub fn type_error(expected: impl Into<String>, actual: impl Into<String>) -> Self {
143        Self::TypeError {
144            expected: expected.into(),
145            actual: actual.into(),
146        }
147    }
148
149    /// Create a new argument error
150    pub fn argument_error(expected: usize, actual: usize) -> Self {
151        Self::ArgumentError { expected, actual }
152    }
153
154    /// Create a new network error
155    pub fn network_error(message: impl Into<String>) -> Self {
156        Self::NetworkError(message.into())
157    }
158
159    /// Create a new AI service error
160    pub fn ai_error(message: impl Into<String>) -> Self {
161        Self::AiError(message.into())
162    }
163
164    /// Create a new internal error
165    pub fn internal_error(message: impl Into<String>) -> Self {
166        Self::InternalError(message.into())
167    }
168
169    /// Check if this is a recoverable error
170    pub fn is_recoverable(&self) -> bool {
171        matches!(
172            self,
173            Error::UndefinedVariable { .. }
174                | Error::UndefinedFunction { .. }
175                | Error::TypeError { .. }
176                | Error::ArgumentError { .. }
177        )
178    }
179
180    /// Get error severity level
181    pub fn severity(&self) -> ErrorSeverity {
182        match self {
183            Error::LexerError { .. } | Error::ParseError { .. } => ErrorSeverity::Fatal,
184            Error::RuntimeError { .. } => ErrorSeverity::Error,
185            Error::UndefinedVariable { .. }
186            | Error::UndefinedFunction { .. }
187            | Error::TypeError { .. }
188            | Error::ArgumentError { .. } => ErrorSeverity::Error,
189            Error::IoError(_) | Error::NetworkError(_) | Error::AiError(_) => ErrorSeverity::Warning,
190            Error::JsonError(_) | Error::InternalError(_) => ErrorSeverity::Fatal,
191        }
192    }
193}
194
195/// Error severity levels
196#[derive(Debug, Clone, Copy, PartialEq, Eq)]
197pub enum ErrorSeverity {
198    /// Fatal errors that prevent execution
199    Fatal,
200    /// Errors that stop current operation
201    Error,
202    /// Warnings that don't stop execution
203    Warning,
204}
205
206impl fmt::Display for ErrorSeverity {
207    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208        match self {
209            ErrorSeverity::Fatal => write!(f, "FATAL"),
210            ErrorSeverity::Error => write!(f, "ERROR"),
211            ErrorSeverity::Warning => write!(f, "WARNING"),
212        }
213    }
214}
215
216#[cfg(test)]
217mod tests {
218    use super::*;
219
220    #[test]
221    fn test_error_creation() {
222        let err = Error::lexer_error(1, 10, "Unexpected character");
223        assert!(matches!(err, Error::LexerError { line: 1, column: 10, .. }));
224        assert_eq!(err.severity(), ErrorSeverity::Fatal);
225    }
226
227    #[test]
228    fn test_error_recoverability() {
229        let recoverable = Error::undefined_variable("x");
230        assert!(recoverable.is_recoverable());
231
232        let fatal = Error::lexer_error(1, 1, "Invalid token");
233        assert!(!fatal.is_recoverable());
234    }
235}