thirtyfour/components/wrapper/
resolver.rsuse crate::components::Component;
use crate::error::WebDriverResult;
use crate::extensions::query::ElementQueryOptions;
use crate::prelude::ElementQueryable;
use crate::{By, ElementQueryFn, WebElement};
use parking_lot::Mutex;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
pub type ElementResolverSingle = ElementResolver<WebElement>;
pub type ElementResolverMulti = ElementResolver<Vec<WebElement>>;
#[macro_export]
macro_rules! resolve {
($a:expr) => {
$a.resolve().await?
};
}
#[macro_export]
macro_rules! resolve_present {
($a:expr) => {
$a.resolve_present().await?
};
}
pub struct ElementResolver<T: Clone> {
base_element: WebElement,
query_fn: Arc<ElementQueryFn<T>>,
element: Arc<Mutex<Option<T>>>,
}
impl<T: Debug + Clone> Debug for ElementResolver<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let element = self.element.lock();
f.debug_struct("ElementResolver")
.field("base_element", &self.base_element)
.field("element", &element)
.finish()
}
}
impl<T: Clone> Clone for ElementResolver<T> {
fn clone(&self) -> Self {
Self {
base_element: self.base_element.clone(),
query_fn: self.query_fn.clone(),
element: self.element.clone(),
}
}
}
impl<T: Clone> ElementResolver<T> {
fn peek(&self) -> Option<T> {
self.element.lock().clone()
}
fn replace(&self, new: T) {
let mut element = self.element.lock();
element.replace(new);
}
pub async fn resolve(&self) -> WebDriverResult<T> {
{
let element = self.element.lock();
if let Some(elem) = element.as_ref() {
return Ok(elem.clone());
}
}
let elem_fut = (self.query_fn)(&self.base_element);
let elem = elem_fut.await?;
self.replace(elem.clone());
Ok(elem)
}
pub fn invalidate(&self) {
self.element.lock().take();
}
pub async fn resolve_force(&self) -> Option<T> {
self.invalidate();
self.resolve().await.ok()
}
}
impl ElementResolver<WebElement> {
pub fn new_single(base_element: WebElement, by: By) -> Self {
let resolver: ElementQueryFn<WebElement> = Box::new(move |elem| {
let by = by.clone();
Box::pin(async move { elem.query(by).single().await })
});
Self::new_custom(base_element, resolver)
}
pub fn new_single_opts(base_element: WebElement, by: By, options: ElementQueryOptions) -> Self {
let resolver: ElementQueryFn<WebElement> = Box::new(move |elem| {
let by = by.clone();
let options = options.clone();
Box::pin(async move { elem.query(by).options(options).single().await })
});
Self::new_custom(base_element, resolver)
}
pub fn new_first(base_element: WebElement, by: By) -> Self {
let resolver: ElementQueryFn<WebElement> = Box::new(move |elem| {
let by = by.clone();
Box::pin(async move { elem.query(by).first().await })
});
Self::new_custom(base_element, resolver)
}
pub fn new_first_opts(base_element: WebElement, by: By, options: ElementQueryOptions) -> Self {
let resolver: ElementQueryFn<WebElement> = Box::new(move |elem| {
let by = by.clone();
let options = options.clone();
Box::pin(async move { elem.query(by).options(options).first().await })
});
Self::new_custom(base_element, resolver)
}
pub fn new_custom(
base_element: WebElement,
custom_resolver_fn: ElementQueryFn<WebElement>,
) -> Self {
Self {
base_element,
query_fn: Arc::new(custom_resolver_fn),
element: Arc::new(Mutex::new(None)),
}
}
pub async fn validate(&self) -> WebDriverResult<Option<WebElement>> {
match self.peek() {
Some(elem) => match elem.is_present().await? {
true => Ok(Some(elem)),
false => {
self.invalidate();
Ok(None)
}
},
None => Ok(None),
}
}
pub async fn resolve_present(&self) -> WebDriverResult<WebElement> {
match self.validate().await? {
Some(elem) => Ok(elem),
None => {
let elem_fut = (self.query_fn)(&self.base_element);
let elem = elem_fut.await?;
self.replace(elem.clone());
Ok(elem)
}
}
}
}
impl ElementResolver<Vec<WebElement>> {
pub fn new_allow_empty(base_element: WebElement, by: By) -> Self {
let resolver: ElementQueryFn<Vec<WebElement>> = Box::new(move |elem| {
let by = by.clone();
Box::pin(async move { elem.query(by).all().await })
});
Self::new_custom(base_element, resolver)
}
pub fn new_allow_empty_opts(
base_element: WebElement,
by: By,
options: ElementQueryOptions,
) -> Self {
let resolver: ElementQueryFn<Vec<WebElement>> = Box::new(move |elem| {
let by = by.clone();
let options = options.clone();
Box::pin(async move { elem.query(by).options(options).all().await })
});
Self::new_custom(base_element, resolver)
}
pub fn new_not_empty(base_element: WebElement, by: By) -> Self {
let resolver: ElementQueryFn<Vec<WebElement>> = Box::new(move |elem| {
let by = by.clone();
Box::pin(async move { elem.query(by).all_required().await })
});
Self::new_custom(base_element, resolver)
}
pub fn new_not_empty_opts(
base_element: WebElement,
by: By,
options: ElementQueryOptions,
) -> Self {
let resolver: ElementQueryFn<Vec<WebElement>> = Box::new(move |elem| {
let by = by.clone();
let options = options.clone();
Box::pin(async move { elem.query(by).options(options).all_required().await })
});
Self::new_custom(base_element, resolver)
}
pub fn new_custom(
base_element: WebElement,
custom_resolver_fn: ElementQueryFn<Vec<WebElement>>,
) -> Self {
Self {
base_element,
query_fn: Arc::new(custom_resolver_fn),
element: Arc::new(Mutex::new(None)),
}
}
pub async fn validate(&self) -> WebDriverResult<Option<Vec<WebElement>>> {
match self.peek() {
Some(elems) => {
for elem in &elems {
if !elem.is_present().await? {
self.invalidate();
return Ok(None);
}
}
Ok(Some(elems))
}
None => Ok(None),
}
}
pub async fn resolve_present(&self) -> WebDriverResult<Vec<WebElement>> {
match self.validate().await? {
Some(elem) => Ok(elem),
None => {
let elem_fut = (self.query_fn)(&self.base_element);
let elem = elem_fut.await?;
self.replace(elem.clone());
Ok(elem)
}
}
}
}
impl<T: Component + Clone> ElementResolver<T> {
pub fn new_single(base_element: WebElement, by: By) -> Self {
let resolver: ElementQueryFn<T> = Box::new(move |elem| {
let by = by.clone();
Box::pin(async move {
let elem = elem.query(by).single().await?;
Ok(elem.into())
})
});
Self::new_custom(base_element, resolver)
}
pub fn new_single_opts(base_element: WebElement, by: By, options: ElementQueryOptions) -> Self {
let resolver: ElementQueryFn<T> = Box::new(move |elem| {
let by = by.clone();
let options = options.clone();
Box::pin(async move {
let elem = elem.query(by).options(options).single().await?;
Ok(elem.into())
})
});
Self::new_custom(base_element, resolver)
}
pub fn new_first(base_element: WebElement, by: By) -> Self {
let resolver: ElementQueryFn<T> = Box::new(move |elem| {
let by = by.clone();
Box::pin(async move {
let elem = elem.query(by).first().await?;
Ok(elem.into())
})
});
Self::new_custom(base_element, resolver)
}
pub fn new_first_opts(base_element: WebElement, by: By, options: ElementQueryOptions) -> Self {
let resolver: ElementQueryFn<T> = Box::new(move |elem| {
let by = by.clone();
let options = options.clone();
Box::pin(async move {
let elem = elem.query(by).options(options).first().await?;
Ok(elem.into())
})
});
Self::new_custom(base_element, resolver)
}
pub fn new_custom(base_element: WebElement, custom_resolver_fn: ElementQueryFn<T>) -> Self {
Self {
base_element,
query_fn: Arc::new(custom_resolver_fn),
element: Arc::new(Mutex::new(None)),
}
}
pub async fn validate(&self) -> WebDriverResult<Option<T>> {
match self.peek() {
Some(component) => match component.base_element().is_present().await? {
true => Ok(Some(component)),
false => {
self.invalidate();
Ok(None)
}
},
None => Ok(None),
}
}
pub async fn resolve_present(&self) -> WebDriverResult<T> {
match self.validate().await? {
Some(component) => Ok(component),
None => {
let comp_fut = (self.query_fn)(&self.base_element);
let comp = comp_fut.await?;
self.replace(comp.clone());
Ok(comp)
}
}
}
}
impl<T: Component + Clone> ElementResolver<Vec<T>> {
pub fn new_allow_empty(base_element: WebElement, by: By) -> Self {
let resolver: ElementQueryFn<Vec<T>> = Box::new(move |elem| {
let by = by.clone();
Box::pin(async move {
let elems = elem.query(by).all().await?;
Ok(elems.into_iter().map(T::from).collect())
})
});
Self::new_custom(base_element, resolver)
}
pub fn new_allow_empty_opts(
base_element: WebElement,
by: By,
options: ElementQueryOptions,
) -> Self {
let resolver: ElementQueryFn<Vec<T>> = Box::new(move |elem| {
let by = by.clone();
let options = options.clone();
Box::pin(async move {
let elems = elem.query(by).options(options).all().await?;
Ok(elems.into_iter().map(T::from).collect())
})
});
Self::new_custom(base_element, resolver)
}
pub fn new_not_empty(base_element: WebElement, by: By) -> Self {
let resolver: ElementQueryFn<Vec<T>> = Box::new(move |elem| {
let by = by.clone();
Box::pin(async move {
let elems = elem.query(by).all_required().await?;
Ok(elems.into_iter().map(T::from).collect())
})
});
Self::new_custom(base_element, resolver)
}
pub fn new_not_empty_opts(
base_element: WebElement,
by: By,
options: ElementQueryOptions,
) -> Self {
let resolver: ElementQueryFn<Vec<T>> = Box::new(move |elem| {
let by = by.clone();
let options = options.clone();
Box::pin(async move {
let elems = elem.query(by).options(options).all_required().await?;
Ok(elems.into_iter().map(T::from).collect())
})
});
Self::new_custom(base_element, resolver)
}
pub fn new_custom(
base_element: WebElement,
custom_resolver_fn: ElementQueryFn<Vec<T>>,
) -> Self {
Self {
base_element,
query_fn: Arc::new(custom_resolver_fn),
element: Arc::new(Mutex::new(None)),
}
}
pub async fn validate(&self) -> WebDriverResult<Option<Vec<T>>> {
match self.peek() {
Some(comps) => {
for comp in &comps {
if !comp.base_element().is_present().await? {
self.invalidate();
return Ok(None);
}
}
Ok(Some(comps))
}
None => Ok(None),
}
}
pub async fn resolve_present(&self) -> WebDriverResult<Vec<T>> {
match self.validate().await? {
Some(comp) => Ok(comp),
None => {
let comp_fut = (self.query_fn)(&self.base_element);
let comp = comp_fut.await?;
self.replace(comp.clone());
Ok(comp)
}
}
}
}