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