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#[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 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 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}