1use ffi;
2use std::any::TypeId;
3use std::error::Error as StdError;
4use std::{fmt, result};
5
6pub type Result<T> = result::Result<T, Error>;
7
8#[derive(Debug)]
9pub struct Error {
10 pub kind: ErrorKind,
12 pub context: Vec<String>,
15}
16
17#[derive(Debug)]
18pub enum ErrorKind {
19 ToJsConversionError {
21 from: &'static str,
23 to: &'static str,
25 },
26 FromJsConversionError {
28 from: &'static str,
30 to: &'static str,
32 },
33 RuntimeError {
35 code: RuntimeErrorCode,
37 name: String,
39 },
40 RecursiveMutCallback,
45 ExternalError(Box<dyn RuntimeError + 'static>),
49 NotAFunction,
51}
52
53impl Error {
54 pub fn external<T: RuntimeError + 'static>(error: T) -> Error {
56 Error {
57 kind: ErrorKind::ExternalError(Box::new(error)),
58 context: vec![],
59 }
60 }
61
62 pub fn from_js_conversion(from: &'static str, to: &'static str) -> Error {
63 Error {
64 kind: ErrorKind::FromJsConversionError { from, to },
65 context: vec![],
66 }
67 }
68
69 pub fn to_js_conversion(from: &'static str, to: &'static str) -> Error {
70 Error {
71 kind: ErrorKind::ToJsConversionError { from, to },
72 context: vec![],
73 }
74 }
75
76 pub fn recursive_mut_callback() -> Error {
77 Error { kind: ErrorKind::RecursiveMutCallback, context: vec![] }
78 }
79
80 pub fn not_a_function() -> Error {
81 Error { kind: ErrorKind::NotAFunction, context: vec![] }
82 }
83
84 pub(crate) fn into_runtime_error_desc(self) -> RuntimeErrorDesc {
85 RuntimeErrorDesc {
86 code: self.runtime_code(),
87 name: self.runtime_name(),
88 message: self.runtime_message(),
89 cause: Box::new(self),
90 }
91 }
92
93 fn runtime_code(&self) -> RuntimeErrorCode {
94 match &self.kind {
95 ErrorKind::ToJsConversionError { .. } => RuntimeErrorCode::TypeError,
96 ErrorKind::FromJsConversionError { .. } => RuntimeErrorCode::TypeError,
97 ErrorKind::NotAFunction => RuntimeErrorCode::TypeError,
98 ErrorKind::ExternalError(err) => err.code(),
99 _ => RuntimeErrorCode::Error
100 }
101 }
102
103 fn runtime_name(&self) -> String {
104 match &self.kind {
105 ErrorKind::ExternalError(err) => err.name(),
106 _ => self.runtime_code().to_string()
107 }
108 }
109
110 fn runtime_message(&self) -> Option<String> {
111 let mut message = String::new();
112
113 for context in self.context.iter().rev() {
114 if !message.is_empty() {
115 message.push_str(": ");
116 }
117
118 message.push_str(context);
119 }
120
121 if let ErrorKind::ExternalError(ref error) = self.kind {
122 if let Some(ref ext_message) = error.message() {
123 if !message.is_empty() {
124 message.push_str(": ");
125 }
126
127 message.push_str(ext_message);
128 }
129 }
130
131 if !message.is_empty() {
132 Some(message)
133 } else {
134 None
135 }
136 }
137}
138
139impl StdError for Error {
140 fn description(&self) -> &'static str {
141 "JavaScript execution error"
142 }
143}
144
145impl fmt::Display for Error {
146 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
147 for context in self.context.iter().rev() {
148 write!(fmt, "{}: ", context)?;
149 }
150
151 match self.kind {
152 ErrorKind::ToJsConversionError { from, to } => {
153 write!(fmt, "error converting {} to JavaScript {}", from, to)
154 },
155 ErrorKind::FromJsConversionError { from, to } => {
156 write!(fmt, "error converting JavaScript {} to {}", from, to)
157 },
158 ErrorKind::RuntimeError { ref name, .. } => {
159 write!(fmt, "JavaScript runtime error ({})", name)
160 },
161 ErrorKind::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
162 ErrorKind::NotAFunction => write!(fmt, "tried to a call a non-function"),
163 ErrorKind::ExternalError(ref err) => err.fmt(fmt),
164 }
165 }
166}
167
168pub trait ResultExt {
169 fn js_err_context<D: fmt::Display>(self, context: D) -> Self;
170 fn js_err_context_with<D: fmt::Display, F: FnOnce(&Error) -> D>(self, op: F) -> Self;
171}
172
173impl<T> ResultExt for result::Result<T, Error> {
174 fn js_err_context<D: fmt::Display>(self, context: D) -> Self {
175 match self {
176 Err(mut err) => {
177 err.context.push(context.to_string());
178 Err(err)
179 },
180 result => result,
181 }
182 }
183
184 fn js_err_context_with<D: fmt::Display, F: FnOnce(&Error) -> D>(self, op: F) -> Self {
185 match self {
186 Err(mut err) => {
187 let context = op(&err).to_string();
188 err.context.push(context);
189 Err(err)
190 },
191 result => result,
192 }
193 }
194}
195
196impl ResultExt for Error {
197 fn js_err_context<D: fmt::Display>(mut self, context: D) -> Self {
198 self.context.push(context.to_string());
199 self
200 }
201
202 fn js_err_context_with<D: fmt::Display, F: FnOnce(&Error) -> D>(mut self, op: F) -> Self {
203 let context = op(&self).to_string();
204 self.context.push(context);
205 self
206 }
207}
208
209pub(crate) struct RuntimeErrorDesc {
210 pub code: RuntimeErrorCode,
211 pub name: String,
212 pub message: Option<String>,
213 pub cause: Box<Error>,
214}
215
216#[derive(Clone, Debug, PartialEq)]
219pub enum RuntimeErrorCode {
220 Error,
221 EvalError,
222 RangeError,
223 ReferenceError,
224 SyntaxError,
225 TypeError,
226 UriError,
227}
228
229impl RuntimeErrorCode {
230 pub(crate) fn from_duk_errcode(code: ffi::duk_errcode_t) -> RuntimeErrorCode {
231 match code as u32 {
232 ffi::DUK_ERR_ERROR => RuntimeErrorCode::Error,
233 ffi::DUK_ERR_EVAL_ERROR => RuntimeErrorCode::EvalError,
234 ffi::DUK_ERR_RANGE_ERROR => RuntimeErrorCode::RangeError,
235 ffi::DUK_ERR_REFERENCE_ERROR => RuntimeErrorCode::ReferenceError,
236 ffi::DUK_ERR_SYNTAX_ERROR => RuntimeErrorCode::SyntaxError,
237 ffi::DUK_ERR_TYPE_ERROR => RuntimeErrorCode::TypeError,
238 ffi::DUK_ERR_URI_ERROR => RuntimeErrorCode::UriError,
239 _ => RuntimeErrorCode::Error,
240 }
241 }
242
243 pub(crate) fn to_duk_errcode(&self) -> ffi::duk_errcode_t {
244 (match *self {
245 RuntimeErrorCode::Error => ffi::DUK_ERR_ERROR,
246 RuntimeErrorCode::EvalError => ffi::DUK_ERR_EVAL_ERROR,
247 RuntimeErrorCode::RangeError => ffi::DUK_ERR_RANGE_ERROR,
248 RuntimeErrorCode::ReferenceError => ffi::DUK_ERR_REFERENCE_ERROR,
249 RuntimeErrorCode::SyntaxError => ffi::DUK_ERR_SYNTAX_ERROR,
250 RuntimeErrorCode::TypeError => ffi::DUK_ERR_TYPE_ERROR,
251 RuntimeErrorCode::UriError => ffi::DUK_ERR_URI_ERROR,
252 }) as ffi::duk_errcode_t
253 }
254}
255
256impl fmt::Display for RuntimeErrorCode {
257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258 match *self {
259 RuntimeErrorCode::Error => write!(f, "Error"),
260 RuntimeErrorCode::EvalError => write!(f, "EvalError"),
261 RuntimeErrorCode::RangeError => write!(f, "RangeError"),
262 RuntimeErrorCode::ReferenceError => write!(f, "ReferenceError"),
263 RuntimeErrorCode::SyntaxError => write!(f, "SyntaxError"),
264 RuntimeErrorCode::TypeError => write!(f, "TypeError"),
265 RuntimeErrorCode::UriError => write!(f, "URIError"),
266 }
267 }
268}
269
270pub trait RuntimeError: fmt::Debug {
272 fn code(&self) -> RuntimeErrorCode {
276 RuntimeErrorCode::Error
277 }
278
279 fn name(&self) -> String {
284 self.code().to_string()
285 }
286
287 fn message(&self) -> Option<String> {
292 None
293 }
294
295 #[doc(hidden)]
302 fn __private_get_type_id__(&self) -> TypeId where Self: 'static {
303 TypeId::of::<Self>()
304 }
305}
306
307impl dyn RuntimeError {
308 pub fn downcast_ref<T: RuntimeError + 'static>(&self) -> Option<&T> {
312 if self.__private_get_type_id__() == TypeId::of::<T>() {
313 unsafe { Some(&*(self as *const dyn RuntimeError as *const T)) }
314 } else {
315 None
316 }
317 }
318}
319
320impl RuntimeError for () {
321}
322
323impl RuntimeError for String {
324 fn message(&self) -> Option<String> {
325 Some(self.clone())
326 }
327}
328
329impl<'a> RuntimeError for &'a str {
330 fn message(&self) -> Option<String> {
331 Some(self.to_string())
332 }
333}
334
335impl<T: RuntimeError + 'static> From<T> for Error {
336 fn from(error: T) -> Error {
337 Error::external(error)
338 }
339}
340
341impl From<ErrorKind> for Error {
342 fn from(error: ErrorKind) -> Error {
343 Error {
344 kind: error,
345 context: vec![],
346 }
347 }
348}