1use std::time::Duration;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
9pub enum LoadMode {
10 #[default]
12 Normal,
13 Eager,
15 None,
17}
18
19impl LoadMode {
20 pub(crate) fn cdp_event(self) -> Option<&'static str> {
22 match self {
23 LoadMode::Normal => Some("Page.loadEventFired"),
24 LoadMode::Eager => Some("Page.domContentEventFired"),
25 LoadMode::None => None,
26 }
27 }
28}
29
30#[derive(Debug, Clone)]
32pub struct GetOptions {
33 pub retry: u32,
35 pub interval: Duration,
37 pub timeout: Option<Duration>,
39 pub load_mode: Option<LoadMode>,
41 pub referer: Option<String>,
43}
44
45impl Default for GetOptions {
46 fn default() -> Self {
47 Self {
48 retry: 0,
49 interval: Duration::from_secs(1),
50 timeout: None,
51 load_mode: None,
52 referer: None,
53 }
54 }
55}
56
57impl GetOptions {
58 pub fn new() -> Self {
59 Self::default()
60 }
61 pub fn retry(mut self, n: u32) -> Self {
62 self.retry = n;
63 self
64 }
65 pub fn interval(mut self, secs: f64) -> Self {
66 self.interval = Duration::from_secs_f64(secs.max(0.0));
67 self
68 }
69 pub fn timeout(mut self, d: Duration) -> Self {
70 self.timeout = Some(d);
71 self
72 }
73 pub fn load_mode(mut self, m: LoadMode) -> Self {
74 self.load_mode = Some(m);
75 self
76 }
77 pub fn referer(mut self, r: impl Into<String>) -> Self {
78 self.referer = Some(r.into());
79 self
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
85pub enum ImageFormat {
86 #[default]
88 Png,
89 Jpeg,
91}
92
93impl ImageFormat {
94 pub(crate) fn cdp_format(self) -> &'static str {
96 match self {
97 ImageFormat::Png => "png",
98 ImageFormat::Jpeg => "jpeg",
99 }
100 }
101}
102
103#[derive(Debug, Clone, Default)]
105pub struct ShotOpts {
106 pub full_page: bool,
108 pub region: Option<((f64, f64), (f64, f64))>,
110 pub format: ImageFormat,
112 pub quality: Option<u8>,
114}
115
116impl ShotOpts {
117 pub fn new() -> Self {
118 Self::default()
119 }
120 pub fn full_page(mut self, yes: bool) -> Self {
121 self.full_page = yes;
122 self
123 }
124 pub fn region(mut self, left_top: (f64, f64), right_bottom: (f64, f64)) -> Self {
125 self.region = Some((left_top, right_bottom));
126 self
127 }
128 pub fn format(mut self, format: ImageFormat) -> Self {
129 self.format = format;
130 self
131 }
132 pub fn quality(mut self, q: u8) -> Self {
133 self.quality = Some(q);
134 self
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq)]
140pub struct PageRect {
141 pub window_width: f64,
143 pub window_height: f64,
145 pub page_width: f64,
147 pub page_height: f64,
149 pub scroll_x: f64,
151 pub scroll_y: f64,
153 pub device_pixel_ratio: f64,
155}
156
157#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
159#[serde(default)]
160pub struct Cookie {
161 pub name: String,
162 pub value: String,
163 pub domain: String,
164 pub path: String,
165 pub expires: f64,
166 pub http_only: bool,
167 pub secure: bool,
168}
169
170#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
172#[serde(default)]
173pub struct CookieParam {
174 pub name: String,
175 pub value: String,
176 pub url: Option<String>,
177 pub domain: Option<String>,
178 pub path: Option<String>,
179 pub secure: Option<bool>,
180 pub http_only: Option<bool>,
181 pub expires: Option<f64>,
182}
183
184#[derive(Debug, Clone, Default)]
186pub struct DialogInfo {
187 pub message: String,
189 pub dialog_type: String,
191 pub default_prompt: String,
193}
194
195#[derive(Debug, Clone, Default)]
197pub struct DownloadInfo {
198 pub url: String,
200 pub suggested_filename: String,
202 pub path: String,
204 pub state: String,
206}
207
208impl CookieParam {
209 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
210 Self {
211 name: name.into(),
212 value: value.into(),
213 ..Default::default()
214 }
215 }
216 pub fn url(mut self, url: impl Into<String>) -> Self {
217 self.url = Some(url.into());
218 self
219 }
220 pub fn domain(mut self, domain: impl Into<String>) -> Self {
221 self.domain = Some(domain.into());
222 self
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 #[test]
231 fn load_mode_maps_to_cdp_wait_event() {
232 assert_eq!(LoadMode::Normal.cdp_event(), Some("Page.loadEventFired"));
233 assert_eq!(
234 LoadMode::Eager.cdp_event(),
235 Some("Page.domContentEventFired")
236 );
237 assert_eq!(LoadMode::None.cdp_event(), None);
238 assert_eq!(LoadMode::default(), LoadMode::Normal);
239 }
240
241 #[test]
242 fn image_format_maps_to_cdp_format() {
243 assert_eq!(ImageFormat::Png.cdp_format(), "png");
244 assert_eq!(ImageFormat::Jpeg.cdp_format(), "jpeg");
245 assert_eq!(ImageFormat::default(), ImageFormat::Png);
246 }
247
248 #[test]
249 fn get_options_builder_sets_fields() {
250 let o = GetOptions::new()
251 .retry(3)
252 .interval(2.5)
253 .timeout(Duration::from_secs(9))
254 .load_mode(LoadMode::Eager)
255 .referer("https://ref.example");
256 assert_eq!(o.retry, 3);
257 assert_eq!(o.interval, Duration::from_secs_f64(2.5));
258 assert_eq!(o.timeout, Some(Duration::from_secs(9)));
259 assert_eq!(o.load_mode, Some(LoadMode::Eager));
260 assert_eq!(o.referer.as_deref(), Some("https://ref.example"));
261 }
262
263 #[test]
264 fn get_options_default_and_negative_interval_clamps() {
265 let d = GetOptions::default();
266 assert_eq!(d.retry, 0);
267 assert_eq!(d.interval, Duration::from_secs(1));
268 assert!(d.timeout.is_none() && d.load_mode.is_none() && d.referer.is_none());
269 assert_eq!(GetOptions::new().interval(-5.0).interval, Duration::ZERO);
271 }
272
273 #[test]
274 fn shot_opts_builder_sets_fields() {
275 let s = ShotOpts::new()
276 .full_page(true)
277 .format(ImageFormat::Jpeg)
278 .quality(80);
279 assert!(s.full_page);
280 assert_eq!(s.format, ImageFormat::Jpeg);
281 assert_eq!(s.quality, Some(80));
282 assert!(s.region.is_none());
283
284 let r = ShotOpts::new().region((1.0, 2.0), (3.0, 4.0));
285 assert_eq!(r.region, Some(((1.0, 2.0), (3.0, 4.0))));
286 }
287
288 #[test]
289 fn cookie_param_builder_sets_identity_fields() {
290 let c = CookieParam::new("sid", "abc")
291 .url("https://x.example")
292 .domain(".example.com");
293 assert_eq!(c.name, "sid");
294 assert_eq!(c.value, "abc");
295 assert_eq!(c.url.as_deref(), Some("https://x.example"));
296 assert_eq!(c.domain.as_deref(), Some(".example.com"));
297 assert!(c.path.is_none() && c.secure.is_none() && c.expires.is_none());
299 }
300}