use tracing::{debug, instrument};
use viewpoint_cdp::protocol::input::DispatchKeyEventParams;
use super::super::Locator;
use crate::error::LocatorError;
use crate::wait::NavigationWaiter;
#[derive(Debug)]
pub struct PressBuilder<'l, 'a> {
locator: &'l Locator<'a>,
key: String,
no_wait_after: bool,
}
impl<'l, 'a> PressBuilder<'l, 'a> {
pub(crate) fn new(locator: &'l Locator<'a>, key: &str) -> Self {
Self {
locator,
key: key.to_string(),
no_wait_after: false,
}
}
#[must_use]
pub fn no_wait_after(mut self, no_wait_after: bool) -> Self {
self.no_wait_after = no_wait_after;
self
}
#[instrument(level = "debug", skip(self), fields(selector = ?self.locator.selector, key = %self.key))]
pub async fn send(self) -> Result<(), LocatorError> {
let navigation_waiter = if self.no_wait_after {
None
} else {
Some(NavigationWaiter::new(
self.locator.page.connection().subscribe_events(),
self.locator.page.session_id().to_string(),
self.locator.page.frame_id().to_string(),
))
};
self.perform_press().await?;
if let Some(waiter) = navigation_waiter {
if let Err(e) = waiter.wait_for_navigation_if_triggered().await {
debug!(error = ?e, "Navigation wait failed after press");
return Err(LocatorError::WaitError(e));
}
}
Ok(())
}
async fn perform_press(&self) -> Result<(), LocatorError> {
self.locator.wait_for_actionable().await?;
debug!(key = %self.key, "Pressing key");
self.locator.focus_element().await?;
let parts: Vec<&str> = self.key.split('+').collect();
let key_ref = self.key.as_str();
let actual_key = *parts.last().unwrap_or(&key_ref);
let mut modifiers = 0;
for part in &parts[..parts.len().saturating_sub(1)] {
match part.to_lowercase().as_str() {
"control" | "ctrl" => {
modifiers |= viewpoint_cdp::protocol::input::modifiers::CTRL;
}
"alt" => modifiers |= viewpoint_cdp::protocol::input::modifiers::ALT,
"shift" => modifiers |= viewpoint_cdp::protocol::input::modifiers::SHIFT,
"meta" | "cmd" => modifiers |= viewpoint_cdp::protocol::input::modifiers::META,
_ => {}
}
}
let mut key_down = DispatchKeyEventParams::key_down(actual_key);
if modifiers != 0 {
key_down.modifiers = Some(modifiers);
}
self.locator.dispatch_key_event(key_down).await?;
let mut key_up = DispatchKeyEventParams::key_up(actual_key);
if modifiers != 0 {
key_up.modifiers = Some(modifiers);
}
self.locator.dispatch_key_event(key_up).await?;
Ok(())
}
}
impl<'l> std::future::IntoFuture for PressBuilder<'l, '_> {
type Output = Result<(), LocatorError>;
type IntoFuture =
std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send + 'l>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(self.send())
}
}