1use pasta_core::parser::Span;
6use std::fmt;
7use thiserror::Error;
8
9#[derive(Error, Debug)]
11pub enum TranspileError {
12 #[error("IO error during transpilation: {0}")]
14 IoError(#[from] std::io::Error),
15
16 #[error("Invalid AST structure at {span}: {message}")]
18 InvalidAst { span: SpanDisplay, message: String },
19
20 #[error("Undefined scene '{name}' at {span}")]
22 UndefinedScene { name: String, span: SpanDisplay },
23
24 #[error("Undefined word '{name}' at {span}")]
26 UndefinedWord { name: String, span: SpanDisplay },
27
28 #[error("Continuation action without actor at {span}")]
30 InvalidContinuation { span: SpanDisplay },
31
32 #[error(
34 "String literal cannot be converted at {span}: dangerous pattern detected in all formats"
35 )]
36 StringLiteralError { text: String, span: SpanDisplay },
37
38 #[error("Too many local variables in scope: {count} (max ~200) at {span}")]
40 TooManyLocalVariables { count: usize, span: SpanDisplay },
41
42 #[error("Unsupported feature: {feature} at {span}")]
44 Unsupported { feature: String, span: SpanDisplay },
45}
46
47impl TranspileError {
48 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 pub fn invalid_continuation(span: &Span) -> Self {
58 TranspileError::InvalidContinuation {
59 span: SpanDisplay::from(*span),
60 }
61 }
62
63 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 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#[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#[derive(Error, Debug, Clone, PartialEq, Eq)]
117pub enum ConfigError {
118 #[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}