Skip to main content

scrapfly_sdk/config/
screenshot.rs

1//! Screenshot endpoint configuration — ported from `sdk/go/config_screenshot.go`.
2
3use crate::enums::{ScreenshotFormat, ScreenshotOption, VisionDeficiencyType};
4use crate::error::ScrapflyError;
5
6use super::url_safe_b64_encode;
7
8/// Configuration for a `POST /screenshot` request.
9#[derive(Debug, Clone, Default)]
10pub struct ScreenshotConfig {
11    /// Target URL (required).
12    pub url: String,
13    /// Image format.
14    pub format: Option<ScreenshotFormat>,
15    /// `fullpage` or a CSS selector.
16    pub capture: Option<String>,
17    /// Viewport resolution (e.g. `1920x1080`).
18    pub resolution: Option<String>,
19    /// Proxy country.
20    pub country: Option<String>,
21    /// Timeout in milliseconds.
22    pub timeout: Option<u32>,
23    /// Extra rendering wait (ms).
24    pub rendering_wait: Option<u32>,
25    /// Wait for CSS selector.
26    pub wait_for_selector: Option<String>,
27    /// Capture options.
28    pub options: Vec<ScreenshotOption>,
29    /// Enable auto-scroll.
30    pub auto_scroll: bool,
31    /// Custom JavaScript (base64url-encoded on the wire).
32    pub js: Option<String>,
33    /// Enable cache.
34    pub cache: bool,
35    /// Cache TTL (seconds).
36    pub cache_ttl: Option<u32>,
37    /// Force cache refresh.
38    pub cache_clear: bool,
39    /// Webhook name.
40    pub webhook: Option<String>,
41    /// Simulated vision deficiency.
42    pub vision_deficiency: Option<VisionDeficiencyType>,
43}
44
45impl ScreenshotConfig {
46    /// Start a builder for `url`.
47    pub fn builder(url: impl Into<String>) -> ScreenshotConfigBuilder {
48        ScreenshotConfigBuilder {
49            cfg: ScreenshotConfig {
50                url: url.into(),
51                ..Default::default()
52            },
53        }
54    }
55
56    /// Serialize into query-param pairs. Mirrors `toAPIParams` in the Go SDK.
57    pub fn to_query_pairs(&self) -> Result<Vec<(String, String)>, ScrapflyError> {
58        if self.url.is_empty() {
59            return Err(ScrapflyError::Config("url is required".into()));
60        }
61        let mut out: Vec<(String, String)> = Vec::new();
62        out.push(("url".into(), self.url.clone()));
63        if let Some(f) = self.format {
64            out.push(("format".into(), f.as_str().into()));
65        }
66        if let Some(c) = &self.capture {
67            out.push(("capture".into(), c.clone()));
68        }
69        if let Some(r) = &self.resolution {
70            out.push(("resolution".into(), r.clone()));
71        }
72        if let Some(c) = &self.country {
73            out.push(("country".into(), c.clone()));
74        }
75        if let Some(t) = self.timeout {
76            out.push(("timeout".into(), t.to_string()));
77        }
78        if let Some(w) = self.rendering_wait {
79            out.push(("rendering_wait".into(), w.to_string()));
80        }
81        if let Some(s) = &self.wait_for_selector {
82            out.push(("wait_for_selector".into(), s.clone()));
83        }
84        if self.auto_scroll {
85            out.push(("auto_scroll".into(), "true".into()));
86        }
87        if let Some(js) = &self.js {
88            out.push(("js".into(), url_safe_b64_encode(js)));
89        }
90        if !self.options.is_empty() {
91            let joined = self
92                .options
93                .iter()
94                .map(|o| o.as_str())
95                .collect::<Vec<_>>()
96                .join(",");
97            out.push(("options".into(), joined));
98        }
99        if self.cache {
100            out.push(("cache".into(), "true".into()));
101            if let Some(ttl) = self.cache_ttl {
102                out.push(("cache_ttl".into(), ttl.to_string()));
103            }
104            if self.cache_clear {
105                out.push(("cache_clear".into(), "true".into()));
106            }
107        }
108        if let Some(wh) = &self.webhook {
109            out.push(("webhook_name".into(), wh.clone()));
110        }
111        if let Some(vd) = self.vision_deficiency {
112            out.push(("vision_deficiency".into(), vd.as_str().into()));
113        }
114        Ok(out)
115    }
116}
117
118/// Builder for [`ScreenshotConfig`].
119#[derive(Debug, Clone)]
120pub struct ScreenshotConfigBuilder {
121    cfg: ScreenshotConfig,
122}
123
124impl ScreenshotConfigBuilder {
125    /// Set output format.
126    pub fn format(mut self, f: ScreenshotFormat) -> Self {
127        self.cfg.format = Some(f);
128        self
129    }
130    /// Set capture target.
131    pub fn capture(mut self, c: impl Into<String>) -> Self {
132        self.cfg.capture = Some(c.into());
133        self
134    }
135    /// Set viewport resolution.
136    pub fn resolution(mut self, r: impl Into<String>) -> Self {
137        self.cfg.resolution = Some(r.into());
138        self
139    }
140    /// Set proxy country.
141    pub fn country(mut self, c: impl Into<String>) -> Self {
142        self.cfg.country = Some(c.into());
143        self
144    }
145    /// Set timeout (ms).
146    pub fn timeout(mut self, t: u32) -> Self {
147        self.cfg.timeout = Some(t);
148        self
149    }
150    /// Set rendering wait (ms).
151    pub fn rendering_wait(mut self, t: u32) -> Self {
152        self.cfg.rendering_wait = Some(t);
153        self
154    }
155    /// Set wait-for-selector.
156    pub fn wait_for_selector(mut self, s: impl Into<String>) -> Self {
157        self.cfg.wait_for_selector = Some(s.into());
158        self
159    }
160    /// Add a capture option.
161    pub fn option(mut self, o: ScreenshotOption) -> Self {
162        self.cfg.options.push(o);
163        self
164    }
165    /// Enable auto-scroll.
166    pub fn auto_scroll(mut self, v: bool) -> Self {
167        self.cfg.auto_scroll = v;
168        self
169    }
170    /// Set custom JS.
171    pub fn js(mut self, js: impl Into<String>) -> Self {
172        self.cfg.js = Some(js.into());
173        self
174    }
175    /// Enable cache.
176    pub fn cache(mut self, v: bool) -> Self {
177        self.cfg.cache = v;
178        self
179    }
180    /// Set cache TTL.
181    pub fn cache_ttl(mut self, v: u32) -> Self {
182        self.cfg.cache_ttl = Some(v);
183        self
184    }
185    /// Force cache refresh.
186    pub fn cache_clear(mut self, v: bool) -> Self {
187        self.cfg.cache_clear = v;
188        self
189    }
190    /// Set webhook name.
191    pub fn webhook(mut self, v: impl Into<String>) -> Self {
192        self.cfg.webhook = Some(v.into());
193        self
194    }
195    /// Set vision deficiency simulation.
196    pub fn vision_deficiency(mut self, v: VisionDeficiencyType) -> Self {
197        self.cfg.vision_deficiency = Some(v);
198        self
199    }
200    /// Finalize the builder.
201    pub fn build(self) -> Result<ScreenshotConfig, ScrapflyError> {
202        if self.cfg.url.is_empty() {
203            return Err(ScrapflyError::Config("url is required".into()));
204        }
205        Ok(self.cfg)
206    }
207}