use futures_util::future::{BoxFuture, FutureExt};
use std::future::Future;
use std::sync::Arc;
use std::{fmt, time::Duration};
use serde::{Deserialize, Serialize};
use crate::WebElement;
use crate::error::WebDriverResult;
mod sealed {
use crate::error::{WebDriverError, WebDriverResult};
use std::borrow::Cow;
use std::rc::Rc;
use std::sync::Arc;
use url::Url;
pub trait IntoTransfer {
fn into(self) -> Arc<str>;
}
pub trait IntoUrl {
fn into_url(self) -> WebDriverResult<Url>;
}
impl IntoTransfer for &str {
fn into(self) -> Arc<str> {
Arc::from(self)
}
}
impl IntoUrl for &str {
fn into_url(self) -> WebDriverResult<Url> {
Url::parse(self).map_err(|e| WebDriverError::ParseError(format!("url parse err {e}")))
}
}
macro_rules! deref_impl {
($trait: path => $meth:ident -> $ret:ty {on} ($($T: ty),*)) => {$(
impl $trait for $T {
#[inline]
fn $meth(self) -> $ret {
<&$T as $trait>::$meth(&self)
}
}
impl $trait for &$T {
#[inline]
fn $meth(self) -> $ret {
<&str as $trait>::$meth(&self)
}
}
)*};
}
deref_impl! {
IntoTransfer => into -> Arc<str> {on} (String, Box<str>, Rc<str>, Cow<'_, str>)
}
deref_impl! {
IntoUrl => into_url -> WebDriverResult<Url> {on} (String)
}
impl IntoTransfer for Arc<str> {
fn into(self) -> Arc<str> {
self
}
}
impl IntoTransfer for &Arc<str> {
fn into(self) -> Arc<str> {
Arc::clone(self)
}
}
impl IntoUrl for Url {
fn into_url(self) -> WebDriverResult<Url> {
Ok(self)
}
}
impl IntoUrl for &Url {
fn into_url(self) -> WebDriverResult<Url> {
Ok(self.clone())
}
}
}
pub trait IntoArcStr: sealed::IntoTransfer {}
impl<T: sealed::IntoTransfer> IntoArcStr for T {}
pub trait IntoUrl: sealed::IntoUrl {}
impl<T: sealed::IntoUrl> IntoUrl for T {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ElementRect {
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
}
impl ElementRect {
pub fn icenter(&self) -> (i64, i64) {
let (x, y) = self.center();
(x as i64, y as i64)
}
pub fn center(&self) -> (f64, f64) {
(self.x + (self.width / 2.0), self.y + (self.height / 2.0))
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ElementRef {
Element {
#[serde(rename = "element-6066-11e4-a52e-4f735466cecf")]
id: String,
},
ShadowElement {
#[serde(rename = "shadow-6066-11e4-a52e-4f735466cecf")]
id: String,
},
}
impl ElementRef {
pub fn id(&self) -> &str {
match self {
ElementRef::Element {
id,
} => id,
ElementRef::ShadowElement {
id,
} => id,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub struct SessionId {
id: Arc<str>,
}
impl<S> From<S> for SessionId
where
S: IntoArcStr,
{
fn from(value: S) -> Self {
SessionId {
id: value.into(),
}
}
}
impl fmt::Display for SessionId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.id)
}
}
impl SessionId {
pub fn null() -> Self {
SessionId {
id: Arc::from(""),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
#[serde(transparent)]
pub struct ElementId {
id: Arc<str>,
}
impl<S> From<S> for ElementId
where
S: IntoArcStr,
{
fn from(value: S) -> Self {
ElementId {
id: value.into(),
}
}
}
impl fmt::Display for ElementId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.id)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub struct WindowHandle {
handle: Arc<str>,
}
impl<S> From<S> for WindowHandle
where
S: IntoArcStr,
{
fn from(value: S) -> Self {
WindowHandle {
handle: value.into(),
}
}
}
impl fmt::Display for WindowHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.handle)
}
}
#[derive(Debug, Clone)]
pub enum WindowType {
Tab,
Window,
}
impl fmt::Display for WindowType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
WindowType::Tab => "tab",
WindowType::Window => "window",
}
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Rect {
pub x: i64,
pub y: i64,
pub width: i64,
pub height: i64,
}
impl Rect {
pub fn new(x: i64, y: i64, width: i64, height: i64) -> Self {
Rect {
x,
y,
width,
height,
}
}
}
pub trait ElementQueryFn<T>: Send + Sync {
type Fut: Future<Output = WebDriverResult<T>> + Send;
fn call(&self, arg: WebElement) -> Self::Fut;
}
impl<T, Fut, Fun> ElementQueryFn<T> for Fun
where
Fun: Fn(WebElement) -> Fut + Send + Sync + ?Sized,
Fut: Future<Output = WebDriverResult<T>> + Send,
{
type Fut = Fut;
fn call(&self, arg: WebElement) -> Fut {
self(arg)
}
}
pub trait ElementPredicate: ElementQueryFn<bool> {}
impl<Fn: ElementQueryFn<bool> + ?Sized> ElementPredicate for Fn {}
pub type DynElementQueryFn<T> = dyn ElementQueryFn<T, Fut = BoxFuture<'static, WebDriverResult<T>>>;
pub type DynElementPredicate = DynElementQueryFn<bool>;
impl<T: 'static> DynElementQueryFn<T> {
fn wrap<F: ElementQueryFn<T, Fut: 'static>>(
fun: F,
) -> impl ElementQueryFn<T, Fut = BoxFuture<'static, WebDriverResult<T>>> {
move |arg: WebElement| fun.call(arg).boxed()
}
pub fn boxed<F: ElementQueryFn<T, Fut: 'static> + 'static>(fun: F) -> Box<Self> {
Box::new(Self::wrap(fun)) as Box<Self>
}
pub fn arc<F: ElementQueryFn<T, Fut: 'static> + 'static>(fun: F) -> Arc<Self> {
Arc::new(Self::wrap(fun)) as Arc<Self>
}
}
#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize)]
pub struct OptionRect {
pub x: Option<i64>,
pub y: Option<i64>,
pub width: Option<i64>,
pub height: Option<i64>,
}
impl OptionRect {
pub fn new() -> Self {
Default::default()
}
pub fn with_x(mut self, value: i64) -> Self {
self.x = Some(value);
self
}
pub fn with_y(mut self, value: i64) -> Self {
self.y = Some(value);
self
}
pub fn with_width(mut self, value: i64) -> Self {
self.width = Some(value);
self
}
pub fn with_height(mut self, value: i64) -> Self {
self.height = Some(value);
self
}
pub fn with_pos(mut self, x: i64, y: i64) -> Self {
self.x = Some(x);
self.y = Some(y);
self
}
pub fn with_size(mut self, width: i64, height: i64) -> Self {
self.width = Some(width);
self.height = Some(height);
self
}
}
impl From<Rect> for OptionRect {
fn from(value: Rect) -> Self {
OptionRect {
x: Some(value.x),
y: Some(value.y),
width: Some(value.width),
height: Some(value.height),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TimeoutConfiguration {
#[serde(skip_serializing_if = "Option::is_none")]
script: Option<u64>,
#[serde(rename = "pageLoad", skip_serializing_if = "Option::is_none")]
page_load: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
implicit: Option<u64>,
}
impl Default for TimeoutConfiguration {
fn default() -> Self {
TimeoutConfiguration::new(
Some(Duration::from_secs(60)),
Some(Duration::from_secs(60)),
Some(Duration::from_secs(0)),
)
}
}
impl TimeoutConfiguration {
pub fn new(
script: Option<Duration>,
page_load: Option<Duration>,
implicit: Option<Duration>,
) -> Self {
TimeoutConfiguration {
script: script.map(|x| x.as_millis() as u64),
page_load: page_load.map(|x| x.as_millis() as u64),
implicit: implicit.map(|x| x.as_millis() as u64),
}
}
pub fn script(&self) -> Option<Duration> {
self.script.map(Duration::from_millis)
}
pub fn set_script(&mut self, timeout: Option<Duration>) {
self.script = timeout.map(|x| x.as_millis() as u64);
}
pub fn page_load(&self) -> Option<Duration> {
self.page_load.map(Duration::from_millis)
}
pub fn set_page_load(&mut self, timeout: Option<Duration>) {
self.page_load = timeout.map(|x| x.as_millis() as u64);
}
pub fn implicit(&self) -> Option<Duration> {
self.implicit.map(Duration::from_millis)
}
pub fn set_implicit(&mut self, timeout: Option<Duration>) {
self.implicit = timeout.map(|x| x.as_millis() as u64);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebDriverStatus {
pub ready: bool,
pub message: String,
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use serde_json::json;
#[test]
fn test_element_ref() {
let id = "daaea226-43aa-400f-896c-210e5af2ac62";
let value = json!({ "element-6066-11e4-a52e-4f735466cecf": id });
let elem_ref: ElementRef = serde_json::from_value(value).unwrap();
assert_matches!(&elem_ref, ElementRef::Element { id: x} if x == id);
assert_eq!(elem_ref.id(), id);
}
#[test]
fn test_shadow_element_ref() {
let id = "daaea226-43aa-400f-896c-210e5af2ac62";
let value = json!({ "shadow-6066-11e4-a52e-4f735466cecf": id });
let elem_ref: ElementRef = serde_json::from_value(value).unwrap();
assert_matches!(&elem_ref, ElementRef::ShadowElement { id: x} if x == id);
assert_eq!(elem_ref.id(), id);
}
}