viewpoint_core/page/keyboard/
builder.rs

1//! Builder for keyboard press operations.
2
3use std::time::Duration;
4
5use tracing::{debug, instrument, trace};
6
7use crate::error::LocatorError;
8use crate::wait::NavigationWaiter;
9
10use super::Keyboard;
11
12/// Builder for keyboard press operations with configurable options.
13///
14/// Created via [`Keyboard::press`].
15#[derive(Debug)]
16pub struct KeyboardPressBuilder<'a> {
17    keyboard: &'a Keyboard,
18    key: String,
19    delay: Option<Duration>,
20    no_wait_after: bool,
21}
22
23impl<'a> KeyboardPressBuilder<'a> {
24    pub(crate) fn new(keyboard: &'a Keyboard, key: &str) -> Self {
25        Self {
26            keyboard,
27            key: key.to_string(),
28            delay: None,
29            no_wait_after: false,
30        }
31    }
32
33    /// Set a delay between key down and key up.
34    #[must_use]
35    pub fn delay(mut self, delay: Duration) -> Self {
36        self.delay = Some(delay);
37        self
38    }
39
40    /// Whether to skip waiting for navigation after the key press.
41    ///
42    /// By default, the press will wait for any triggered navigation to complete.
43    /// Set to `true` to return immediately after the key is pressed.
44    #[must_use]
45    pub fn no_wait_after(mut self, no_wait_after: bool) -> Self {
46        self.no_wait_after = no_wait_after;
47        self
48    }
49
50    /// Execute the press operation.
51    #[instrument(level = "debug", skip(self), fields(key = %self.key))]
52    pub async fn send(self) -> Result<(), LocatorError> {
53        // Set up navigation waiter before the action if needed
54        let navigation_waiter = if self.no_wait_after {
55            None
56        } else {
57            Some(NavigationWaiter::new(
58                self.keyboard.connection().subscribe_events(),
59                self.keyboard.session_id().to_string(),
60                self.keyboard.frame_id().to_string(),
61            ))
62        };
63
64        // Perform the press action
65        self.keyboard.press_internal(&self.key, self.delay).await?;
66
67        // Wait for navigation if triggered
68        if let Some(waiter) = navigation_waiter {
69            match waiter.wait_for_navigation_if_triggered().await {
70                Ok(navigated) => {
71                    if navigated {
72                        trace!("Navigation completed after keyboard press");
73                    }
74                }
75                Err(e) => {
76                    debug!(error = ?e, "Navigation wait failed after keyboard press");
77                    return Err(LocatorError::WaitError(e));
78                }
79            }
80        }
81
82        Ok(())
83    }
84}
85
86impl<'a> std::future::IntoFuture for KeyboardPressBuilder<'a> {
87    type Output = Result<(), LocatorError>;
88    type IntoFuture =
89        std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send + 'a>>;
90
91    fn into_future(self) -> Self::IntoFuture {
92        Box::pin(self.send())
93    }
94}