Skip to main content

pasta_lua/
error.rs

1//! Error types for Pasta Lua transpiler.
2//!
3//! This module defines error types for the Lua code generation process.
4
5use pasta_core::parser::Span;
6use std::fmt;
7use thiserror::Error;
8
9/// Transpile error type for Lua code generation.
10#[derive(Error, Debug)]
11pub enum TranspileError {
12    /// IO error during transpilation (writing to output).
13    #[error("IO error during transpilation: {0}")]
14    IoError(#[from] std::io::Error),
15
16    /// Invalid AST structure.
17    #[error("Invalid AST structure at {span}: {message}")]
18    InvalidAst { span: SpanDisplay, message: String },
19
20    /// Undefined scene reference.
21    #[error("Undefined scene '{name}' at {span}")]
22    UndefinedScene { name: String, span: SpanDisplay },
23
24    /// Undefined word reference.
25    #[error("Undefined word '{name}' at {span}")]
26    UndefinedWord { name: String, span: SpanDisplay },
27
28    /// Continuation action without actor.
29    #[error("Continuation action without actor at {span}")]
30    InvalidContinuation { span: SpanDisplay },
31
32    /// String literal cannot be converted.
33    #[error(
34        "String literal cannot be converted at {span}: dangerous pattern detected in all formats"
35    )]
36    StringLiteralError { text: String, span: SpanDisplay },
37
38    /// Too many local variables.
39    #[error("Too many local variables in scope: {count} (max ~200) at {span}")]
40    TooManyLocalVariables { count: usize, span: SpanDisplay },
41
42    /// Unsupported feature.
43    #[error("Unsupported feature: {feature} at {span}")]
44    Unsupported { feature: String, span: SpanDisplay },
45}
46
47impl TranspileError {
48    /// Create an InvalidAst error.
49    pub fn invalid_ast(span: &Span, message: &str) -> Self {
50        TranspileError::InvalidAst {
51            span: SpanDisplay::from(*span),
52            message: message.to_string(),
53        }
54    }
55
56    /// Create an InvalidContinuation error.
57    pub fn invalid_continuation(span: &Span) -> Self {
58        TranspileError::InvalidContinuation {
59            span: SpanDisplay::from(*span),
60        }
61    }
62
63    /// Create a StringLiteralError.
64    pub fn string_literal_error(span: &Span, text: &str) -> Self {
65        TranspileError::StringLiteralError {
66            text: text.to_string(),
67            span: SpanDisplay::from(*span),
68        }
69    }
70
71    /// Create an Unsupported error.
72    pub fn unsupported(span: &Span, feature: &str) -> Self {
73        TranspileError::Unsupported {
74            span: SpanDisplay::from(*span),
75            feature: feature.to_string(),
76        }
77    }
78}
79
80/// Wrapper for Span with Display implementation.
81///
82/// Format: `[L{start_line}:{start_col}-L{end_line}:{end_col}]`
83#[derive(Debug, Clone, Copy)]
84pub struct SpanDisplay {
85    pub start_line: usize,
86    pub start_col: usize,
87    pub end_line: usize,
88    pub end_col: usize,
89}
90
91impl From<Span> for SpanDisplay {
92    fn from(span: Span) -> Self {
93        SpanDisplay {
94            start_line: span.start_line,
95            start_col: span.start_col,
96            end_line: span.end_line,
97            end_col: span.end_col,
98        }
99    }
100}
101
102impl fmt::Display for SpanDisplay {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        write!(
105            f,
106            "[L{}:{}-L{}:{}]",
107            self.start_line, self.start_col, self.end_line, self.end_col
108        )
109    }
110}
111
112/// Configuration errors for Lua library settings.
113///
114/// These errors occur during configuration parsing and validation,
115/// particularly for the `[lua]` section's `libs` array.
116#[derive(Error, Debug, Clone, PartialEq, Eq)]
117pub enum ConfigError {
118    /// Unknown library name in libs array.
119    #[error(
120        "Unknown library: {0}. Valid libraries: std_all, std_all_unsafe, std_coroutine, std_table, std_io, std_os, std_string, std_utf8, std_math, std_package, std_debug, assertions, testing, env, regex, json, yaml"
121    )]
122    UnknownLibrary(String),
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn test_span_display_format() {
131        let span = Span {
132            start_line: 10,
133            start_col: 5,
134            end_line: 10,
135            end_col: 23,
136            start_byte: 0,
137            end_byte: 18,
138        };
139        let display = SpanDisplay::from(span);
140        assert_eq!(format!("{}", display), "[L10:5-L10:23]");
141    }
142
143    #[test]
144    fn test_span_display_multiline() {
145        let span = Span {
146            start_line: 1,
147            start_col: 1,
148            end_line: 5,
149            end_col: 10,
150            start_byte: 0,
151            end_byte: 50,
152        };
153        let display = SpanDisplay::from(span);
154        assert_eq!(format!("{}", display), "[L1:1-L5:10]");
155    }
156
157    #[test]
158    fn test_transpile_error_io() {
159        let err =
160            TranspileError::IoError(std::io::Error::new(std::io::ErrorKind::Other, "test error"));
161        assert!(format!("{}", err).contains("IO error"));
162    }
163
164    #[test]
165    fn test_transpile_error_invalid_ast() {
166        let span = Span::new(1, 1, 1, 10, 0, 10);
167        let err = TranspileError::invalid_ast(&span, "test message");
168        let msg = format!("{}", err);
169        assert!(msg.contains("Invalid AST structure"));
170        assert!(msg.contains("[L1:1-L1:10]"));
171        assert!(msg.contains("test message"));
172    }
173
174    #[test]
175    fn test_transpile_error_invalid_continuation() {
176        let span = Span::new(25, 1, 25, 8, 0, 8);
177        let err = TranspileError::invalid_continuation(&span);
178        let msg = format!("{}", err);
179        assert!(msg.contains("Continuation action without actor"));
180        assert!(msg.contains("[L25:1-L25:8]"));
181    }
182}