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("value is not callable: {0}")]
221 NotCallable(String),
222 #[error("native `{name}` expected {expected} args, got {got}")]
223 NativeArity {
224 name: Symbol,
225 expected: usize,
226 got: usize,
227 },
228 #[error("expected {expected}, got {got}")]
229 NativeType { expected: String, got: String },
230 #[error("pattern match failure")]
231 MatchFailure,
232 #[error("expected boolean, got {0}")]
233 ExpectedBool(String),
234 #[error("type error: {0}")]
235 Type(#[from] TypeError),
236 #[error("ambiguous overload for `{name}`")]
237 AmbiguousOverload { name: Symbol },
238 #[error("no native implementation for `{name}` with type {typ}")]
239 MissingImpl { name: Symbol, typ: String },
240 #[error("ambiguous native implementation for `{name}` with type {typ}")]
241 AmbiguousImpl { name: Symbol, typ: String },
242 #[error("duplicate native implementation for `{name}` with type {typ}")]
243 DuplicateImpl { name: Symbol, typ: String },
244 #[error("no type class instance for `{class}` with type {typ}")]
245 MissingTypeclassImpl { class: Symbol, typ: String },
246 #[error("ambiguous type class instance for `{class}` with type {typ}")]
247 AmbiguousTypeclassImpl { class: Symbol, typ: String },
248 #[error("duplicate type class instance for `{class}` with type {typ}")]
249 DuplicateTypeclassImpl { class: Symbol, typ: String },
250 #[error("injected `{name}` has incompatible type {typ}")]
251 InvalidInjection { name: Symbol, typ: String },
252 #[error("unknown type for value in `{0}`")]
253 UnknownType(Symbol),
254 #[error("unknown field `{field}` on {value}")]
255 UnknownField { field: Symbol, value: String },
256 #[error("unsupported expression")]
257 UnsupportedExpr,
258 #[error("empty sequence")]
259 EmptySequence,
260 #[error("index {index} out of bounds in `{name}` (len {len})")]
261 IndexOutOfBounds {
262 name: Symbol,
263 index: i32,
264 len: usize,
265 },
266 #[error("internal error: {0}")]
267 Internal(String),
268 #[error(transparent)]
269 Library(#[from] Box<LibraryError>),
270 #[error("cancelled")]
271 Cancelled,
272 #[error("{0}")]
273 OutOfGas(#[from] OutOfGas),
274 #[error("{0}")]
275 Custom(String),
276 #[error("Evaluation suspended")]
277 Suspended,
278}
279
280impl From<LibraryError> for EngineError {
281 fn from(err: LibraryError) -> Self {
282 EngineError::Library(Box::new(err))
283 }
284}
285
286impl From<&str> for EngineError {
287 fn from(msg: &str) -> Self {
288 EngineError::Custom(msg.to_string())
289 }
290}
291
292impl From<String> for EngineError {
293 fn from(msg: String) -> Self {
294 EngineError::Custom(msg)
295 }
296}