Skip to main content

headless_chrome/
util.rs

1use std::time::{Duration, Instant};
2use std::{collections::HashMap, thread::sleep};
3
4use anyhow::{Error, Result};
5
6use thiserror::Error;
7
8use crate::protocol::cdp::Runtime::RemoteObject;
9
10use crate::browser::tab::point::Point;
11
12#[derive(Debug, Error)]
13#[error("The event waited for never came")]
14pub struct Timeout;
15
16/// A helper to wait until some event has passed.
17#[derive(Debug)]
18pub struct Wait {
19    timeout: Duration,
20    sleep: Duration,
21}
22
23impl Default for Wait {
24    fn default() -> Self {
25        Self {
26            timeout: Duration::from_secs(10),
27            sleep: Duration::from_millis(100),
28        }
29    }
30}
31
32pub fn extract_midpoint(remote_obj: RemoteObject) -> Result<Point> {
33    let mut prop_map = HashMap::new();
34
35    let points = remote_obj.preview.map(|v| {
36        for prop in v.properties {
37            prop_map.insert(prop.name, prop.value.unwrap().parse::<f64>().unwrap());
38        }
39        Point {
40            x: prop_map["x"] + (prop_map["width"] / 2.0),
41            y: prop_map["y"] + (prop_map["height"] / 2.0),
42        }
43    });
44
45    match points {
46        Some(v) => Ok(v),
47        None => Ok(Point { x: 0.0, y: 0.0 }),
48    }
49}
50
51impl Wait {
52    pub fn new(timeout: Duration, sleep: Duration) -> Self {
53        Self { timeout, sleep }
54    }
55
56    pub fn with_timeout(timeout: Duration) -> Self {
57        Self {
58            timeout,
59            ..Self::default()
60        }
61    }
62
63    pub fn with_sleep(sleep: Duration) -> Self {
64        Self {
65            sleep,
66            ..Self::default()
67        }
68    }
69
70    pub fn forever() -> Self {
71        Self {
72            timeout: Duration::from_secs(u64::MAX),
73            ..Self::default()
74        }
75    }
76
77    /// Wait until the given predicate returns `Some(G)` or timeout arrives.
78    ///
79    /// Note: If your predicate function shadows potential unexpected
80    ///   errors you should consider using `#strict_until`.
81    pub fn until<F, G>(&self, predicate: F) -> Result<G, Timeout>
82    where
83        F: FnMut() -> Option<G>,
84    {
85        let mut predicate = predicate;
86        let start = Instant::now();
87        loop {
88            if let Some(v) = predicate() {
89                return Ok(v);
90            }
91            if start.elapsed() > self.timeout {
92                return Err(Timeout);
93            }
94            sleep(self.sleep);
95        }
96    }
97
98    /// Wait until the given predicate returns `Ok(G)`, an unexpected error occurs or timeout arrives.
99    ///
100    /// Errors produced by the predicate are downcasted by the additional provided closure.
101    /// If the downcast is successful - the error is ignored, otherwise the wait is terminated
102    /// and `Err(error)` containing the unexpected failure is returned to the caller.
103    ///
104    /// You can use `failure::Error::downcast::<YourStructName>` out-of-the-box,
105    /// if you need to ignore one expected error, or you can implement a matching closure
106    /// that responds to multiple error types.
107    pub fn strict_until<F, D, E, G>(&self, predicate: F, downcast: D) -> Result<G>
108    where
109        F: FnMut() -> Result<G>,
110        D: FnMut(Error) -> Result<E>,
111    {
112        let mut predicate = predicate;
113        let mut downcast = downcast;
114        let start = Instant::now();
115        loop {
116            match predicate() {
117                Ok(value) => return Ok(value),
118                Err(error) => downcast(error)?,
119            };
120
121            if start.elapsed() > self.timeout {
122                return Err(Timeout.into());
123            }
124            sleep(self.sleep);
125        }
126    }
127}