scrapfly_sdk/config/
screenshot.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
//! Screenshot endpoint configuration — ported from `sdk/go/config_screenshot.go`.

use crate::enums::{ScreenshotFormat, ScreenshotOption, VisionDeficiencyType};
use crate::error::ScrapflyError;

use super::url_safe_b64_encode;

/// Configuration for a `POST /screenshot` request.
#[derive(Debug, Clone, Default)]
pub struct ScreenshotConfig {
    /// Target URL (required).
    pub url: String,
    /// Image format.
    pub format: Option<ScreenshotFormat>,
    /// `fullpage` or a CSS selector.
    pub capture: Option<String>,
    /// Viewport resolution (e.g. `1920x1080`).
    pub resolution: Option<String>,
    /// Proxy country.
    pub country: Option<String>,
    /// Timeout in milliseconds.
    pub timeout: Option<u32>,
    /// Extra rendering wait (ms).
    pub rendering_wait: Option<u32>,
    /// Wait for CSS selector.
    pub wait_for_selector: Option<String>,
    /// Capture options.
    pub options: Vec<ScreenshotOption>,
    /// Enable auto-scroll.
    pub auto_scroll: bool,
    /// Custom JavaScript (base64url-encoded on the wire).
    pub js: Option<String>,
    /// Enable cache.
    pub cache: bool,
    /// Cache TTL (seconds).
    pub cache_ttl: Option<u32>,
    /// Force cache refresh.
    pub cache_clear: bool,
    /// Webhook name.
    pub webhook: Option<String>,
    /// Simulated vision deficiency.
    pub vision_deficiency: Option<VisionDeficiencyType>,
}

impl ScreenshotConfig {
    /// Start a builder for `url`.
    pub fn builder(url: impl Into<String>) -> ScreenshotConfigBuilder {
        ScreenshotConfigBuilder {
            cfg: ScreenshotConfig {
                url: url.into(),
                ..Default::default()
            },
        }
    }

    /// Serialize into query-param pairs. Mirrors `toAPIParams` in the Go SDK.
    pub fn to_query_pairs(&self) -> Result<Vec<(String, String)>, ScrapflyError> {
        if self.url.is_empty() {
            return Err(ScrapflyError::Config("url is required".into()));
        }
        let mut out: Vec<(String, String)> = Vec::new();
        out.push(("url".into(), self.url.clone()));
        if let Some(f) = self.format {
            out.push(("format".into(), f.as_str().into()));
        }
        if let Some(c) = &self.capture {
            out.push(("capture".into(), c.clone()));
        }
        if let Some(r) = &self.resolution {
            out.push(("resolution".into(), r.clone()));
        }
        if let Some(c) = &self.country {
            out.push(("country".into(), c.clone()));
        }
        if let Some(t) = self.timeout {
            out.push(("timeout".into(), t.to_string()));
        }
        if let Some(w) = self.rendering_wait {
            out.push(("rendering_wait".into(), w.to_string()));
        }
        if let Some(s) = &self.wait_for_selector {
            out.push(("wait_for_selector".into(), s.clone()));
        }
        if self.auto_scroll {
            out.push(("auto_scroll".into(), "true".into()));
        }
        if let Some(js) = &self.js {
            out.push(("js".into(), url_safe_b64_encode(js)));
        }
        if !self.options.is_empty() {
            let joined = self
                .options
                .iter()
                .map(|o| o.as_str())
                .collect::<Vec<_>>()
                .join(",");
            out.push(("options".into(), joined));
        }
        if self.cache {
            out.push(("cache".into(), "true".into()));
            if let Some(ttl) = self.cache_ttl {
                out.push(("cache_ttl".into(), ttl.to_string()));
            }
            if self.cache_clear {
                out.push(("cache_clear".into(), "true".into()));
            }
        }
        if let Some(wh) = &self.webhook {
            out.push(("webhook_name".into(), wh.clone()));
        }
        if let Some(vd) = self.vision_deficiency {
            out.push(("vision_deficiency".into(), vd.as_str().into()));
        }
        Ok(out)
    }
}

/// Builder for [`ScreenshotConfig`].
#[derive(Debug, Clone)]
pub struct ScreenshotConfigBuilder {
    cfg: ScreenshotConfig,
}

impl ScreenshotConfigBuilder {
    /// Set output format.
    pub fn format(mut self, f: ScreenshotFormat) -> Self {
        self.cfg.format = Some(f);
        self
    }
    /// Set capture target.
    pub fn capture(mut self, c: impl Into<String>) -> Self {
        self.cfg.capture = Some(c.into());
        self
    }
    /// Set viewport resolution.
    pub fn resolution(mut self, r: impl Into<String>) -> Self {
        self.cfg.resolution = Some(r.into());
        self
    }
    /// Set proxy country.
    pub fn country(mut self, c: impl Into<String>) -> Self {
        self.cfg.country = Some(c.into());
        self
    }
    /// Set timeout (ms).
    pub fn timeout(mut self, t: u32) -> Self {
        self.cfg.timeout = Some(t);
        self
    }
    /// Set rendering wait (ms).
    pub fn rendering_wait(mut self, t: u32) -> Self {
        self.cfg.rendering_wait = Some(t);
        self
    }
    /// Set wait-for-selector.
    pub fn wait_for_selector(mut self, s: impl Into<String>) -> Self {
        self.cfg.wait_for_selector = Some(s.into());
        self
    }
    /// Add a capture option.
    pub fn option(mut self, o: ScreenshotOption) -> Self {
        self.cfg.options.push(o);
        self
    }
    /// Enable auto-scroll.
    pub fn auto_scroll(mut self, v: bool) -> Self {
        self.cfg.auto_scroll = v;
        self
    }
    /// Set custom JS.
    pub fn js(mut self, js: impl Into<String>) -> Self {
        self.cfg.js = Some(js.into());
        self
    }
    /// Enable cache.
    pub fn cache(mut self, v: bool) -> Self {
        self.cfg.cache = v;
        self
    }
    /// Set cache TTL.
    pub fn cache_ttl(mut self, v: u32) -> Self {
        self.cfg.cache_ttl = Some(v);
        self
    }
    /// Force cache refresh.
    pub fn cache_clear(mut self, v: bool) -> Self {
        self.cfg.cache_clear = v;
        self
    }
    /// Set webhook name.
    pub fn webhook(mut self, v: impl Into<String>) -> Self {
        self.cfg.webhook = Some(v.into());
        self
    }
    /// Set vision deficiency simulation.
    pub fn vision_deficiency(mut self, v: VisionDeficiencyType) -> Self {
        self.cfg.vision_deficiency = Some(v);
        self
    }
    /// Finalize the builder.
    pub fn build(self) -> Result<ScreenshotConfig, ScrapflyError> {
        if self.cfg.url.is_empty() {
            return Err(ScrapflyError::Config("url is required".into()));
        }
        Ok(self.cfg)
    }
}