1 use hyper::status::StatusCode;
2 use rustc_serialize::base64::FromBase64Error;
3 use rustc_serialize::json::{DecoderError, Json, ParserError, ToJson};
4 use std::borrow::Cow;
5 use std::collections::BTreeMap;
6 use std::convert::From;
7 use std::error::Error;
8 use std::fmt;
9 use std::io;
10 
11 #[derive(Debug, PartialEq)]
12 pub enum ErrorStatus {
13     /// The [`ElementClick`] command could not be completed because the
14     /// [element] receiving the events is obscuring the element that was
15     /// requested clicked.
16     ///
17     /// [`ElementClick`]:
18     /// ../command/enum.WebDriverCommand.html#variant.ElementClick
19     /// [element]: ../common/struct.WebElement.html
20     ElementClickIntercepted,
21 
22     /// A [command] could not be completed because the element is not pointer-
23     /// or keyboard interactable.
24     ///
25     /// [command]: ../command/index.html
26     ElementNotInteractable,
27 
28     /// An attempt was made to select an [element] that cannot be selected.
29     ///
30     /// [element]: ../common/struct.WebElement.html
31     ElementNotSelectable,
32 
33     /// Navigation caused the user agent to hit a certificate warning, which is
34     /// usually the result of an expired or invalid TLS certificate.
35     InsecureCertificate,
36 
37     /// The arguments passed to a [command] are either invalid or malformed.
38     ///
39     /// [command]: ../command/index.html
40     InvalidArgument,
41 
42     /// An illegal attempt was made to set a cookie under a different domain
43     /// than the current page.
44     InvalidCookieDomain,
45 
46     /// The coordinates provided to an interactions operation are invalid.
47     InvalidCoordinates,
48 
49     /// A [command] could not be completed because the element is an invalid
50     /// state, e.g. attempting to click an element that is no longer attached
51     /// to the document.
52     ///
53     /// [command]: ../command/index.html
54     InvalidElementState,
55 
56     /// Argument was an invalid selector.
57     InvalidSelector,
58 
59     /// Occurs if the given session ID is not in the list of active sessions,
60     /// meaning the session either does not exist or that it’s not active.
61     InvalidSessionId,
62 
63     /// An error occurred while executing JavaScript supplied by the user.
64     JavascriptError,
65 
66     /// The target for mouse interaction is not in the browser’s viewport and
67     /// cannot be brought into that viewport.
68     MoveTargetOutOfBounds,
69 
70     /// An attempt was made to operate on a modal dialogue when one was not
71     /// open.
72     NoSuchAlert,
73 
74     /// No cookie matching the given path name was found amongst the associated
75     /// cookies of the current browsing context’s active document.
76     NoSuchCookie,
77 
78     /// An [element] could not be located on the page using the given search
79     /// parameters.
80     ///
81     /// [element]: ../common/struct.WebElement.html
82     NoSuchElement,
83 
84     /// A [command] to switch to a frame could not be satisfied because the
85     /// frame could not be found.
86     ///
87     /// [command]: ../command/index.html
88     NoSuchFrame,
89 
90     /// A [command] to switch to a window could not be satisfied because the
91     /// window could not be found.
92     ///
93     /// [command]: ../command/index.html
94     NoSuchWindow,
95 
96     /// A script did not complete before its timeout expired.
97     ScriptTimeout,
98 
99     /// A new session could not be created.
100     SessionNotCreated,
101 
102     /// A [command] failed because the referenced [element] is no longer
103     /// attached to the DOM.
104     ///
105     /// [command]: ../command/index.html
106     /// [element]: ../common/struct.WebElement.html
107     StaleElementReference,
108 
109     /// An operation did not complete before its timeout expired.
110     Timeout,
111 
112     /// A screen capture was made impossible.
113     UnableToCaptureScreen,
114 
115     /// Setting the cookie’s value could not be done.
116     UnableToSetCookie,
117 
118     /// A modal dialogue was open, blocking this operation.
119     UnexpectedAlertOpen,
120 
121     /// The requested command could not be executed because it does not exist.
122     UnknownCommand,
123 
124     /// An unknown error occurred in the remote end whilst processing the
125     /// [command].
126     ///
127     /// [command]: ../command/index.html
128     UnknownError,
129 
130     /// The requested [command] matched a known endpoint, but did not match a
131     /// method for that endpoint.
132     ///
133     /// [command]: ../command/index.html
134     UnknownMethod,
135 
136     UnknownPath,
137 
138     /// Indicates that a [command] that should have executed properly is not
139     /// currently supported.
140     UnsupportedOperation,
141 }
142 
143 impl ErrorStatus {
144     /// Returns the string serialisation of the error type.
error_code(&self) -> &'static str145     pub fn error_code(&self) -> &'static str {
146         use self::ErrorStatus::*;
147         match *self {
148             ElementClickIntercepted => "element click intercepted",
149             ElementNotInteractable => "element not interactable",
150             ElementNotSelectable => "element not selectable",
151             InsecureCertificate => "insecure certificate",
152             InvalidArgument => "invalid argument",
153             InvalidCookieDomain => "invalid cookie domain",
154             InvalidCoordinates => "invalid coordinates",
155             InvalidElementState => "invalid element state",
156             InvalidSelector => "invalid selector",
157             InvalidSessionId => "invalid session id",
158             JavascriptError => "javascript error",
159             MoveTargetOutOfBounds => "move target out of bounds",
160             NoSuchAlert => "no such alert",
161             NoSuchCookie => "no such cookie",
162             NoSuchElement => "no such element",
163             NoSuchFrame => "no such frame",
164             NoSuchWindow => "no such window",
165             ScriptTimeout => "script timeout",
166             SessionNotCreated => "session not created",
167             StaleElementReference => "stale element reference",
168             Timeout => "timeout",
169             UnableToCaptureScreen => "unable to capture screen",
170             UnableToSetCookie => "unable to set cookie",
171             UnexpectedAlertOpen => "unexpected alert open",
172             UnknownCommand |
173             UnknownError => "unknown error",
174             UnknownMethod => "unknown method",
175             UnknownPath => "unknown command",
176             UnsupportedOperation => "unsupported operation",
177         }
178     }
179 
180     /// Returns the correct HTTP status code associated with the error type.
http_status(&self) -> StatusCode181     pub fn http_status(&self) -> StatusCode {
182         use self::ErrorStatus::*;
183         use self::StatusCode::*;
184         match *self {
185             ElementClickIntercepted => BadRequest,
186             ElementNotInteractable => BadRequest,
187             ElementNotSelectable => BadRequest,
188             InsecureCertificate => BadRequest,
189             InvalidArgument => BadRequest,
190             InvalidCookieDomain => BadRequest,
191             InvalidCoordinates => BadRequest,
192             InvalidElementState => BadRequest,
193             InvalidSelector => BadRequest,
194             InvalidSessionId => NotFound,
195             JavascriptError => InternalServerError,
196             MoveTargetOutOfBounds => InternalServerError,
197             NoSuchAlert => NotFound,
198             NoSuchCookie => NotFound,
199             NoSuchElement => NotFound,
200             NoSuchFrame => NotFound,
201             NoSuchWindow => NotFound,
202             ScriptTimeout => RequestTimeout,
203             SessionNotCreated => InternalServerError,
204             StaleElementReference => NotFound,
205             Timeout => RequestTimeout,
206             UnableToCaptureScreen => BadRequest,
207             UnableToSetCookie => InternalServerError,
208             UnexpectedAlertOpen => InternalServerError,
209             UnknownCommand => NotFound,
210             UnknownError => InternalServerError,
211             UnknownMethod => MethodNotAllowed,
212             UnknownPath => NotFound,
213             UnsupportedOperation => InternalServerError,
214         }
215     }
216 }
217 
218 /// Deserialises error type from string.
219 impl From<String> for ErrorStatus {
from(s: String) -> ErrorStatus220     fn from(s: String) -> ErrorStatus {
221         use self::ErrorStatus::*;
222         match &*s {
223             "element click intercepted" => ElementClickIntercepted,
224             "element not interactable" | "element not visible" => ElementNotInteractable,
225             "element not selectable" => ElementNotSelectable,
226             "insecure certificate" => InsecureCertificate,
227             "invalid argument" => InvalidArgument,
228             "invalid cookie domain" => InvalidCookieDomain,
229             "invalid coordinates" | "invalid element coordinates" => InvalidCoordinates,
230             "invalid element state" => InvalidElementState,
231             "invalid selector" => InvalidSelector,
232             "invalid session id" => InvalidSessionId,
233             "javascript error" => JavascriptError,
234             "move target out of bounds" => MoveTargetOutOfBounds,
235             "no such alert" => NoSuchAlert,
236             "no such element" => NoSuchElement,
237             "no such frame" => NoSuchFrame,
238             "no such window" => NoSuchWindow,
239             "script timeout" => ScriptTimeout,
240             "session not created" => SessionNotCreated,
241             "stale element reference" => StaleElementReference,
242             "timeout" => Timeout,
243             "unable to capture screen" => UnableToCaptureScreen,
244             "unable to set cookie" => UnableToSetCookie,
245             "unexpected alert open" => UnexpectedAlertOpen,
246             "unknown command" => UnknownCommand,
247             "unknown error" => UnknownError,
248             "unsupported operation" => UnsupportedOperation,
249             _ => UnknownError,
250         }
251     }
252 }
253 
254 pub type WebDriverResult<T> = Result<T, WebDriverError>;
255 
256 #[derive(Debug)]
257 pub struct WebDriverError {
258     pub error: ErrorStatus,
259     pub message: Cow<'static, str>,
260     pub stack: Cow<'static, str>,
261     pub delete_session: bool,
262 }
263 
264 impl WebDriverError {
new<S>(error: ErrorStatus, message: S) -> WebDriverError where S: Into<Cow<'static, str>>265     pub fn new<S>(error: ErrorStatus, message: S) -> WebDriverError
266         where S: Into<Cow<'static, str>>
267     {
268         WebDriverError {
269             error: error,
270             message: message.into(),
271             stack: "".into(),
272             delete_session: false,
273         }
274     }
275 
new_with_stack<S>(error: ErrorStatus, message: S, stack: S) -> WebDriverError where S: Into<Cow<'static, str>>276     pub fn new_with_stack<S>(error: ErrorStatus, message: S, stack: S) -> WebDriverError
277         where S: Into<Cow<'static, str>>
278     {
279         WebDriverError {
280             error: error,
281             message: message.into(),
282             stack: stack.into(),
283             delete_session: false,
284         }
285     }
286 
error_code(&self) -> &'static str287     pub fn error_code(&self) -> &'static str {
288         self.error.error_code()
289     }
290 
http_status(&self) -> StatusCode291     pub fn http_status(&self) -> StatusCode {
292         self.error.http_status()
293     }
294 
to_json_string(&self) -> String295     pub fn to_json_string(&self) -> String {
296         self.to_json().to_string()
297     }
298 }
299 
300 impl ToJson for WebDriverError {
to_json(&self) -> Json301     fn to_json(&self) -> Json {
302         let mut data = BTreeMap::new();
303         data.insert("error".into(), self.error_code().to_json());
304         data.insert("message".into(), self.message.to_json());
305         data.insert("stacktrace".into(), self.stack.to_json());
306 
307         let mut wrapper = BTreeMap::new();
308         wrapper.insert("value".into(), Json::Object(data));
309         Json::Object(wrapper)
310     }
311 }
312 
313 impl Error for WebDriverError {
description(&self) -> &str314     fn description(&self) -> &str {
315         self.error_code()
316     }
317 
cause(&self) -> Option<&Error>318     fn cause(&self) -> Option<&Error> {
319         None
320     }
321 }
322 
323 impl fmt::Display for WebDriverError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result324     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325         self.message.fmt(f)
326     }
327 }
328 
329 impl From<ParserError> for WebDriverError {
from(err: ParserError) -> WebDriverError330     fn from(err: ParserError) -> WebDriverError {
331         WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
332     }
333 }
334 
335 impl From<io::Error> for WebDriverError {
from(err: io::Error) -> WebDriverError336     fn from(err: io::Error) -> WebDriverError {
337         WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
338     }
339 }
340 
341 impl From<DecoderError> for WebDriverError {
from(err: DecoderError) -> WebDriverError342     fn from(err: DecoderError) -> WebDriverError {
343         WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
344     }
345 }
346 
347 impl From<FromBase64Error> for WebDriverError {
from(err: FromBase64Error) -> WebDriverError348     fn from(err: FromBase64Error) -> WebDriverError {
349         WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
350     }
351 }
352 
353 impl From<Box<Error>> for WebDriverError {
from(err: Box<Error>) -> WebDriverError354     fn from(err: Box<Error>) -> WebDriverError {
355         WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
356     }
357 }
358