1use std::path::PathBuf;
2use std::process::ExitStatus;
3
4use rexlang_ast::expr::Symbol;
5use rexlang_lexer::LexicalError;
6use rexlang_parser::error::ParserErr;
7use rexlang_typesystem::TypeError;
8use rexlang_util::OutOfGas;
9
10use crate::libraries::LibraryId;
11
12#[derive(Debug)]
13pub enum LibraryError {
14 NotFound {
15 library_name: String,
16 },
17 NoBaseDirectory,
18 ImportEscapesRoot,
19 EmptyLibraryPath,
20 StatePoisoned,
21 CyclicImport {
22 id: LibraryId,
23 },
24 InvalidIncludeRoot {
25 path: PathBuf,
26 source: std::io::Error,
27 },
28 InvalidLibraryPath {
29 path: PathBuf,
30 source: std::io::Error,
31 },
32 ReadFailed {
33 path: PathBuf,
34 source: std::io::Error,
35 },
36 NotUtf8 {
37 kind: &'static str,
38 path: PathBuf,
39 source: std::string::FromUtf8Error,
40 },
41 NotUtf8Remote {
42 url: String,
43 source: std::string::FromUtf8Error,
44 },
45 ShaMismatchStdlib {
46 library: String,
47 expected: String,
48 actual: String,
49 },
50 ShaMismatchPath {
51 kind: &'static str,
52 path: PathBuf,
53 expected: String,
54 actual: String,
55 },
56 MissingExport {
57 library: Symbol,
58 export: Symbol,
59 },
60 DuplicateImportedName {
61 name: Symbol,
62 },
63 ImportNameConflictsWithLocal {
64 library: Symbol,
65 name: Symbol,
66 },
67 Lex {
68 source: LexicalError,
69 },
70 LexInLibrary {
71 library: LibraryId,
72 source: LexicalError,
73 },
74 Parse {
75 errors: Vec<ParserErr>,
76 },
77 ParseInLibrary {
78 library: LibraryId,
79 errors: Vec<ParserErr>,
80 },
81 TopLevelExprInLibrary {
82 library: LibraryId,
83 },
84 InvalidGithubImport {
85 url: String,
86 },
87 UnpinnedGithubImport {
88 url: String,
89 },
90 CurlFailed {
91 source: std::io::Error,
92 },
93 CurlNonZeroExit {
94 url: String,
95 status: ExitStatus,
96 },
97}
98
99impl std::fmt::Display for LibraryError {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 match self {
102 LibraryError::NotFound { library_name } => {
103 write!(f, "library not found: {library_name}")
104 }
105 LibraryError::NoBaseDirectory => {
106 write!(f, "cannot resolve local import without a base directory")
107 }
108 LibraryError::ImportEscapesRoot => write!(f, "import path escapes filesystem root"),
109 LibraryError::EmptyLibraryPath => write!(f, "empty library path"),
110 LibraryError::StatePoisoned => write!(f, "library state poisoned"),
111 LibraryError::CyclicImport { id } => write!(f, "cyclic library import: {id}"),
112 LibraryError::InvalidIncludeRoot { path, source } => {
113 write!(f, "invalid include root `{}`: {source}", path.display())
114 }
115 LibraryError::InvalidLibraryPath { path, source } => {
116 write!(f, "invalid library path `{}`: {source}", path.display())
117 }
118 LibraryError::ReadFailed { path, source } => {
119 write!(f, "failed to read library `{}`: {source}", path.display())
120 }
121 LibraryError::NotUtf8 { kind, path, source } => {
122 write!(
123 f,
124 "{kind} library `{}` was not utf-8: {source}",
125 path.display()
126 )
127 }
128 LibraryError::NotUtf8Remote { url, source } => {
129 write!(f, "remote library `{url}` was not utf-8: {source}")
130 }
131 LibraryError::ShaMismatchStdlib {
132 library,
133 expected,
134 actual,
135 } => write!(
136 f,
137 "sha mismatch for `{library}`: expected #{expected}, got #{actual}"
138 ),
139 LibraryError::ShaMismatchPath {
140 kind,
141 path,
142 expected,
143 actual,
144 } => write!(
145 f,
146 "{kind} import sha mismatch for {}: expected #{expected}, got #{actual}",
147 path.display()
148 ),
149 LibraryError::MissingExport { library, export } => {
150 write!(f, "library `{library}` does not export `{export}`")
151 }
152 LibraryError::DuplicateImportedName { name } => {
153 write!(f, "duplicate imported name `{name}`")
154 }
155 LibraryError::ImportNameConflictsWithLocal { library, name } => {
156 write!(
157 f,
158 "imported name `{name}` from library `{library}` conflicts with local declaration"
159 )
160 }
161 LibraryError::Lex { source } => write!(f, "lex error: {source}"),
162 LibraryError::LexInLibrary { library, source } => {
163 write!(f, "lex error in library {library}: {source}")
164 }
165 LibraryError::Parse { errors } => {
166 write!(f, "parse error:")?;
167 for err in errors {
168 write!(f, "\n {err}")?;
169 }
170 Ok(())
171 }
172 LibraryError::ParseInLibrary { library, errors } => {
173 write!(f, "parse error in library {library}:")?;
174 for err in errors {
175 write!(f, "\n {err}")?;
176 }
177 Ok(())
178 }
179 LibraryError::TopLevelExprInLibrary { library } => {
180 write!(
181 f,
182 "library {library} cannot contain a top-level expression; library files must be declaration-only"
183 )
184 }
185 LibraryError::InvalidGithubImport { url } => write!(
186 f,
187 "github import must be `https://github.com/<owner>/<repo>/<path>.rex#<sha>` (got {url})"
188 ),
189 LibraryError::UnpinnedGithubImport { url } => {
190 write!(f, "github import must be pinned: add `#<sha>` (got {url})")
191 }
192 LibraryError::CurlFailed { source } => write!(f, "failed to run curl: {source}"),
193 LibraryError::CurlNonZeroExit { url, status } => {
194 write!(f, "failed to fetch {url} (curl exit {status})")
195 }
196 }
197 }
198}
199
200impl std::error::Error for LibraryError {
201 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
202 match self {
203 LibraryError::InvalidIncludeRoot { source, .. } => Some(source),
204 LibraryError::InvalidLibraryPath { source, .. } => Some(source),
205 LibraryError::ReadFailed { source, .. } => Some(source),
206 LibraryError::NotUtf8 { source, .. } => Some(source),
207 LibraryError::NotUtf8Remote { source, .. } => Some(source),
208 LibraryError::Lex { source } => Some(source),
209 LibraryError::LexInLibrary { source, .. } => Some(source),
210 LibraryError::CurlFailed { source } => Some(source),
211 _ => None,
212 }
213 }
214}
215
216#[derive(Debug, thiserror::Error)]
217pub enum EngineError {
218 #[error("unknown variable `{0}`")]
219 UnknownVar(Symbol),
220 #[error(
221 "runtime environment is incompatible with the compiled program (expected ABI {expected_abi_version}, got {actual_abi_version}; missing natives: {missing_natives:?}; incompatible natives: {incompatible_natives:?}; missing class methods: {missing_class_methods:?}; incompatible class methods: {incompatible_class_methods:?})"
222 )]
223 Link {
224 expected_abi_version: u32,
225 actual_abi_version: u32,
226 missing_natives: Vec<Symbol>,
227 incompatible_natives: Vec<Symbol>,
228 missing_class_methods: Vec<Symbol>,
229 incompatible_class_methods: Vec<Symbol>,
230 },
231 #[error("value is not callable: {0}")]
232 NotCallable(String),
233 #[error("native `{name}` expected {expected} args, got {got}")]
234 NativeArity {
235 name: Symbol,
236 expected: usize,
237 got: usize,
238 },
239 #[error("expected {expected}, got {got}")]
240 NativeType { expected: String, got: String },
241 #[error("pattern match failure")]
242 MatchFailure,
243 #[error("expected boolean, got {0}")]
244 ExpectedBool(String),
245 #[error("type error: {0}")]
246 Type(#[from] TypeError),
247 #[error("ambiguous overload for `{name}`")]
248 AmbiguousOverload { name: Symbol },
249 #[error("no native implementation for `{name}` with type {typ}")]
250 MissingImpl { name: Symbol, typ: String },
251 #[error("ambiguous native implementation for `{name}` with type {typ}")]
252 AmbiguousImpl { name: Symbol, typ: String },
253 #[error("duplicate native implementation for `{name}` with type {typ}")]
254 DuplicateImpl { name: Symbol, typ: String },
255 #[error("no type class instance for `{class}` with type {typ}")]
256 MissingTypeclassImpl { class: Symbol, typ: String },
257 #[error("ambiguous type class instance for `{class}` with type {typ}")]
258 AmbiguousTypeclassImpl { class: Symbol, typ: String },
259 #[error("duplicate type class instance for `{class}` with type {typ}")]
260 DuplicateTypeclassImpl { class: Symbol, typ: String },
261 #[error("injected `{name}` has incompatible type {typ}")]
262 InvalidInjection { name: Symbol, typ: String },
263 #[error("unknown type for value in `{0}`")]
264 UnknownType(Symbol),
265 #[error("unknown field `{field}` on {value}")]
266 UnknownField { field: Symbol, value: String },
267 #[error("unsupported expression")]
268 UnsupportedExpr,
269 #[error("empty sequence")]
270 EmptySequence,
271 #[error("index {index} out of bounds in `{name}` (len {len})")]
272 IndexOutOfBounds {
273 name: Symbol,
274 index: i32,
275 len: usize,
276 },
277 #[error("internal error: {0}")]
278 Internal(String),
279 #[error(transparent)]
280 Library(#[from] Box<LibraryError>),
281 #[error("cancelled")]
282 Cancelled,
283 #[error("{0}")]
284 OutOfGas(#[from] OutOfGas),
285 #[error("{0}")]
286 Custom(String),
287 #[error("Evaluation suspended")]
288 Suspended,
289}
290
291#[derive(Debug, thiserror::Error)]
292#[error(transparent)]
293pub struct CompileError(#[from] EngineError);
294
295impl CompileError {
296 pub fn as_engine_error(&self) -> &EngineError {
297 &self.0
298 }
299
300 pub fn into_engine_error(self) -> EngineError {
301 self.0
302 }
303}
304
305#[derive(Debug, thiserror::Error)]
306#[error(transparent)]
307pub struct EvalError(#[from] EngineError);
308
309impl EvalError {
310 pub fn as_engine_error(&self) -> &EngineError {
311 &self.0
312 }
313
314 pub fn into_engine_error(self) -> EngineError {
315 self.0
316 }
317}
318
319#[derive(Debug, thiserror::Error)]
320pub enum ExecutionError {
321 #[error(transparent)]
322 Compile(#[from] CompileError),
323 #[error(transparent)]
324 Eval(#[from] EvalError),
325}
326
327impl ExecutionError {
328 pub fn as_engine_error(&self) -> &EngineError {
329 match self {
330 ExecutionError::Compile(err) => err.as_engine_error(),
331 ExecutionError::Eval(err) => err.as_engine_error(),
332 }
333 }
334
335 pub fn into_engine_error(self) -> EngineError {
336 match self {
337 ExecutionError::Compile(err) => err.into_engine_error(),
338 ExecutionError::Eval(err) => err.into_engine_error(),
339 }
340 }
341}
342
343impl From<LibraryError> for EngineError {
344 fn from(err: LibraryError) -> Self {
345 EngineError::Library(Box::new(err))
346 }
347}
348
349impl From<&str> for EngineError {
350 fn from(msg: &str) -> Self {
351 EngineError::Custom(msg.to_string())
352 }
353}
354
355impl From<String> for EngineError {
356 fn from(msg: String) -> Self {
357 EngineError::Custom(msg)
358 }
359}