use base64::DecodeError;
use http::StatusCode;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use std::borrow::Cow;
use std::convert::From;
use std::error::Error;
use std::fmt;
use std::io;
#[derive(Debug, PartialEq)]
pub enum ErrorStatus {
DetachedShadowRoot,
ElementClickIntercepted,
ElementNotInteractable,
ElementNotSelectable,
InsecureCertificate,
InvalidArgument,
InvalidCookieDomain,
InvalidCoordinates,
InvalidElementState,
InvalidSelector,
InvalidSessionId,
JavascriptError,
MoveTargetOutOfBounds,
NoSuchAlert,
NoSuchCookie,
NoSuchElement,
NoSuchFrame,
NoSuchShadowRoot,
NoSuchWindow,
ScriptTimeout,
SessionNotCreated,
StaleElementReference,
Timeout,
UnableToCaptureScreen,
UnableToSetCookie,
UnexpectedAlertOpen,
UnknownCommand,
UnknownError,
UnknownMethod,
UnknownPath,
UnsupportedOperation,
}
impl Serialize for ErrorStatus {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.error_code().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ErrorStatus {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let error_string = String::deserialize(deserializer)?;
Ok(ErrorStatus::from(error_string))
}
}
impl ErrorStatus {
pub fn error_code(&self) -> &'static str {
use self::ErrorStatus::*;
match *self {
DetachedShadowRoot => "detached shadow root",
ElementClickIntercepted => "element click intercepted",
ElementNotInteractable => "element not interactable",
ElementNotSelectable => "element not selectable",
InsecureCertificate => "insecure certificate",
InvalidArgument => "invalid argument",
InvalidCookieDomain => "invalid cookie domain",
InvalidCoordinates => "invalid coordinates",
InvalidElementState => "invalid element state",
InvalidSelector => "invalid selector",
InvalidSessionId => "invalid session id",
JavascriptError => "javascript error",
MoveTargetOutOfBounds => "move target out of bounds",
NoSuchAlert => "no such alert",
NoSuchCookie => "no such cookie",
NoSuchElement => "no such element",
NoSuchFrame => "no such frame",
NoSuchShadowRoot => "no such shadow root",
NoSuchWindow => "no such window",
ScriptTimeout => "script timeout",
SessionNotCreated => "session not created",
StaleElementReference => "stale element reference",
Timeout => "timeout",
UnableToCaptureScreen => "unable to capture screen",
UnableToSetCookie => "unable to set cookie",
UnexpectedAlertOpen => "unexpected alert open",
UnknownCommand | UnknownError => "unknown error",
UnknownMethod => "unknown method",
UnknownPath => "unknown command",
UnsupportedOperation => "unsupported operation",
}
}
pub fn http_status(&self) -> StatusCode {
use self::ErrorStatus::*;
match *self {
DetachedShadowRoot => StatusCode::NOT_FOUND,
ElementClickIntercepted => StatusCode::BAD_REQUEST,
ElementNotInteractable => StatusCode::BAD_REQUEST,
ElementNotSelectable => StatusCode::BAD_REQUEST,
InsecureCertificate => StatusCode::BAD_REQUEST,
InvalidArgument => StatusCode::BAD_REQUEST,
InvalidCookieDomain => StatusCode::BAD_REQUEST,
InvalidCoordinates => StatusCode::BAD_REQUEST,
InvalidElementState => StatusCode::BAD_REQUEST,
InvalidSelector => StatusCode::BAD_REQUEST,
InvalidSessionId => StatusCode::NOT_FOUND,
JavascriptError => StatusCode::INTERNAL_SERVER_ERROR,
MoveTargetOutOfBounds => StatusCode::INTERNAL_SERVER_ERROR,
NoSuchAlert => StatusCode::NOT_FOUND,
NoSuchCookie => StatusCode::NOT_FOUND,
NoSuchElement => StatusCode::NOT_FOUND,
NoSuchFrame => StatusCode::NOT_FOUND,
NoSuchShadowRoot => StatusCode::NOT_FOUND,
NoSuchWindow => StatusCode::NOT_FOUND,
ScriptTimeout => StatusCode::INTERNAL_SERVER_ERROR,
SessionNotCreated => StatusCode::INTERNAL_SERVER_ERROR,
StaleElementReference => StatusCode::NOT_FOUND,
Timeout => StatusCode::INTERNAL_SERVER_ERROR,
UnableToCaptureScreen => StatusCode::BAD_REQUEST,
UnableToSetCookie => StatusCode::INTERNAL_SERVER_ERROR,
UnexpectedAlertOpen => StatusCode::INTERNAL_SERVER_ERROR,
UnknownCommand => StatusCode::NOT_FOUND,
UnknownError => StatusCode::INTERNAL_SERVER_ERROR,
UnknownMethod => StatusCode::METHOD_NOT_ALLOWED,
UnknownPath => StatusCode::NOT_FOUND,
UnsupportedOperation => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
impl From<String> for ErrorStatus {
fn from(s: String) -> ErrorStatus {
use self::ErrorStatus::*;
match &*s {
"detached shadow root" => DetachedShadowRoot,
"element click intercepted" => ElementClickIntercepted,
"element not interactable" | "element not visible" => ElementNotInteractable,
"element not selectable" => ElementNotSelectable,
"insecure certificate" => InsecureCertificate,
"invalid argument" => InvalidArgument,
"invalid cookie domain" => InvalidCookieDomain,
"invalid coordinates" | "invalid element coordinates" => InvalidCoordinates,
"invalid element state" => InvalidElementState,
"invalid selector" => InvalidSelector,
"invalid session id" => InvalidSessionId,
"javascript error" => JavascriptError,
"move target out of bounds" => MoveTargetOutOfBounds,
"no such alert" => NoSuchAlert,
"no such element" => NoSuchElement,
"no such frame" => NoSuchFrame,
"no such shadow root" => NoSuchShadowRoot,
"no such window" => NoSuchWindow,
"script timeout" => ScriptTimeout,
"session not created" => SessionNotCreated,
"stale element reference" => StaleElementReference,
"timeout" => Timeout,
"unable to capture screen" => UnableToCaptureScreen,
"unable to set cookie" => UnableToSetCookie,
"unexpected alert open" => UnexpectedAlertOpen,
"unknown command" => UnknownCommand,
"unknown error" => UnknownError,
"unsupported operation" => UnsupportedOperation,
_ => UnknownError,
}
}
}
pub type WebDriverResult<T> = Result<T, WebDriverError>;
#[derive(Debug, PartialEq, Serialize)]
#[serde(remote = "Self")]
pub struct WebDriverError {
pub error: ErrorStatus,
pub message: Cow<'static, str>,
#[serde(rename = "stacktrace")]
pub stack: Cow<'static, str>,
#[serde(skip)]
pub delete_session: bool,
}
impl Serialize for WebDriverError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[derive(Serialize)]
struct Wrapper<'a> {
#[serde(with = "WebDriverError")]
value: &'a WebDriverError,
}
Wrapper { value: self }.serialize(serializer)
}
}
impl WebDriverError {
pub fn new<S>(error: ErrorStatus, message: S) -> WebDriverError
where
S: Into<Cow<'static, str>>,
{
WebDriverError {
error,
message: message.into(),
stack: "".into(),
delete_session: false,
}
}
pub fn new_with_stack<S>(error: ErrorStatus, message: S, stack: S) -> WebDriverError
where
S: Into<Cow<'static, str>>,
{
WebDriverError {
error,
message: message.into(),
stack: stack.into(),
delete_session: false,
}
}
pub fn error_code(&self) -> &'static str {
self.error.error_code()
}
pub fn http_status(&self) -> StatusCode {
self.error.http_status()
}
}
impl Error for WebDriverError {
fn description(&self) -> &str {
self.error_code()
}
fn cause(&self) -> Option<&dyn Error> {
None
}
}
impl fmt::Display for WebDriverError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.message.fmt(f)
}
}
impl From<serde_json::Error> for WebDriverError {
fn from(err: serde_json::Error) -> WebDriverError {
WebDriverError::new(ErrorStatus::InvalidArgument, err.to_string())
}
}
impl From<io::Error> for WebDriverError {
fn from(err: io::Error) -> WebDriverError {
WebDriverError::new(ErrorStatus::UnknownError, err.to_string())
}
}
impl From<DecodeError> for WebDriverError {
fn from(err: DecodeError) -> WebDriverError {
WebDriverError::new(ErrorStatus::UnknownError, err.to_string())
}
}
impl From<Box<dyn Error>> for WebDriverError {
fn from(err: Box<dyn Error>) -> WebDriverError {
WebDriverError::new(ErrorStatus::UnknownError, err.to_string())
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
use crate::test::assert_ser;
#[test]
fn test_json_webdriver_error() {
let json = json!({"value": {
"error": "unknown error",
"message": "foo bar",
"stacktrace": "foo\nbar",
}});
let error = WebDriverError {
error: ErrorStatus::UnknownError,
message: "foo bar".into(),
stack: "foo\nbar".into(),
delete_session: true,
};
assert_ser(&error, json);
}
#[test]
fn test_json_error_status() {
assert_ser(&ErrorStatus::UnknownError, json!("unknown error"));
}
}