1use serde::{Deserialize, Deserializer, Serialize};
5use serde_json::{Map, Value};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
13pub enum SolariRegion {
14 #[default]
16 UsWest,
17}
18
19impl SolariRegion {
20 pub fn base_url(&self) -> &'static str {
22 match self {
23 SolariRegion::UsWest => "https://api.getsolari.com",
24 }
25 }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
34pub enum SameSite {
35 Strict,
36 Lax,
37 None,
38}
39
40#[derive(Debug, Clone, Default, Serialize, Deserialize)]
42#[serde(rename_all = "camelCase")]
43pub struct Cookie {
44 pub name: String,
45 pub value: String,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub domain: Option<String>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub path: Option<String>,
50 #[serde(default, skip_serializing_if = "Option::is_none")]
51 pub expires: Option<f64>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub http_only: Option<bool>,
54 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub secure: Option<bool>,
56 #[serde(default, skip_serializing_if = "Option::is_none")]
57 pub same_site: Option<SameSite>,
58}
59
60#[derive(Debug, Clone, Default, Serialize, Deserialize)]
62pub struct LocalStorageEntry {
63 pub name: String,
64 pub value: String,
65}
66
67#[derive(Debug, Clone, Default, Serialize, Deserialize)]
69#[serde(rename_all = "camelCase")]
70pub struct StorageOrigin {
71 pub origin: String,
72 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub local_storage: Option<Vec<LocalStorageEntry>>,
74}
75
76#[derive(Debug, Clone, Default, Serialize, Deserialize)]
79#[serde(rename_all = "camelCase")]
80pub struct StorageState {
81 #[serde(default, skip_serializing_if = "Option::is_none")]
82 pub cookies: Option<Vec<Cookie>>,
83 #[serde(default, skip_serializing_if = "Option::is_none")]
84 pub origins: Option<Vec<StorageOrigin>>,
85 #[serde(flatten)]
87 pub extra: Map<String, Value>,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
96#[serde(rename_all = "lowercase")]
97pub enum ProxyTier {
98 Residential,
100 Static,
102 Mobile,
104}
105
106#[derive(Debug, Clone, Default, Serialize)]
108#[serde(rename_all = "camelCase")]
109pub struct ProxyRequest {
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub country: Option<String>,
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub tier: Option<ProxyTier>,
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub asn: Option<String>,
119 #[serde(skip_serializing_if = "Option::is_none")]
122 pub session: Option<String>,
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub session_duration: Option<u32>,
126 #[serde(skip_serializing_if = "Option::is_none")]
128 pub state: Option<String>,
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub city: Option<String>,
132}
133
134impl ProxyRequest {
135 pub fn country(code: impl Into<String>) -> Self {
137 ProxyRequest {
138 country: Some(code.into()),
139 ..Default::default()
140 }
141 }
142
143 pub fn tier(mut self, tier: ProxyTier) -> Self {
144 self.tier = Some(tier);
145 self
146 }
147
148 pub fn asn(mut self, asn: impl Into<String>) -> Self {
149 self.asn = Some(asn.into());
150 self
151 }
152
153 pub fn session(mut self, id: impl Into<String>) -> Self {
155 self.session = Some(id.into());
156 self
157 }
158
159 pub fn session_duration(mut self, minutes: u32) -> Self {
160 self.session_duration = Some(minutes);
161 self
162 }
163
164 pub fn state(mut self, state: impl Into<String>) -> Self {
165 self.state = Some(state.into());
166 self
167 }
168
169 pub fn city(mut self, city: impl Into<String>) -> Self {
170 self.city = Some(city.into());
171 self
172 }
173}
174
175#[derive(Debug, Clone, Serialize)]
187#[serde(untagged)]
188pub enum ProxySpec {
189 Shorthand(String),
191 Request(ProxyRequest),
193}
194
195impl ProxySpec {
196 pub fn country(code: impl Into<String>) -> Self {
198 ProxySpec::Shorthand(code.into())
199 }
200
201 pub fn off() -> Self {
203 ProxySpec::Shorthand("off".to_string())
204 }
205
206 pub fn smart() -> Self {
208 ProxySpec::Shorthand("smart".to_string())
209 }
210}
211
212impl From<ProxyRequest> for ProxySpec {
213 fn from(r: ProxyRequest) -> Self {
214 ProxySpec::Request(r)
215 }
216}
217
218#[derive(Debug, Clone, Deserialize)]
220#[serde(rename_all = "camelCase")]
221pub struct ResolvedProxyConfig {
222 pub server: String,
223 pub username: String,
224 pub password: String,
225 pub timezone_id: String,
226 pub country: String,
227 #[serde(default)]
228 pub tier: Option<ProxyTier>,
229}
230
231#[derive(Debug, Clone, Default, Deserialize)]
234pub struct ProxyCountries {
235 #[serde(default)]
238 pub enabled: bool,
239 #[serde(default)]
241 pub countries: Vec<String>,
242}
243
244#[derive(Debug, Clone)]
250pub struct Session {
251 pub id: String,
252 pub ws_endpoint: String,
254 pub cdp_endpoint: String,
257 pub expires_at: String,
259 pub storage_state: Option<Option<StorageState>>,
264 pub proxy: Option<ResolvedProxyConfig>,
266}
267
268#[derive(Debug, Clone, Default, Deserialize)]
271#[serde(rename_all = "camelCase")]
272pub struct SessionView {
273 #[serde(default)]
274 pub session_id: Option<String>,
275 #[serde(default)]
276 pub status: Option<String>,
277 #[serde(default)]
278 pub expires_at: Option<String>,
279 #[serde(flatten)]
280 pub extra: Map<String, Value>,
281}
282
283#[derive(Debug, Clone)]
285pub struct ReplayUrl {
286 pub url: String,
287 pub expires_in_seconds: u64,
289 pub content_encoding: String,
291}
292
293#[derive(Debug, Clone, Deserialize)]
299#[serde(rename_all = "camelCase")]
300pub struct Profile {
301 pub id: String,
302 #[serde(default)]
303 pub name: String,
304 #[serde(flatten)]
306 pub extra: Map<String, Value>,
307}
308
309#[derive(Debug, Clone, Default)]
311pub struct ProfileSaveResult {
312 pub version: u64,
314 pub size_bytes: u64,
316}
317
318#[derive(Debug, Clone, Default, Serialize)]
325#[serde(rename_all = "camelCase")]
326pub(crate) struct CreateSessionRequest {
327 #[serde(skip_serializing_if = "Option::is_none")]
328 pub profile_id: Option<String>,
329 #[serde(skip_serializing_if = "is_false")]
330 pub recording: bool,
331 #[serde(skip_serializing_if = "is_false")]
332 pub stealth: bool,
333 #[serde(skip_serializing_if = "is_false")]
334 pub captcha: bool,
335 #[serde(skip_serializing_if = "is_false")]
336 pub web_bot_auth: bool,
337 #[serde(skip_serializing_if = "Option::is_none")]
338 pub proxy: Option<ProxySpec>,
339}
340
341fn is_false(b: &bool) -> bool {
342 !*b
343}
344
345#[derive(Debug, Clone, Deserialize)]
347#[serde(rename_all = "camelCase")]
348pub(crate) struct StorageStateUrl {
349 #[serde(default)]
350 pub url: Option<String>,
351 #[allow(dead_code)]
352 #[serde(default)]
353 pub expires_in_seconds: Option<u64>,
354}
355
356#[derive(Debug, Clone, Deserialize)]
358#[serde(rename_all = "camelCase")]
359pub(crate) struct CreateSessionResponse {
360 #[serde(default)]
361 pub session_id: Option<String>,
362 #[serde(default)]
363 pub ws_endpoint: Option<String>,
364 #[serde(default)]
365 pub cdp_endpoint: Option<String>,
366 #[serde(default)]
367 pub expires_at: Option<String>,
368 #[serde(default, deserialize_with = "double_option")]
370 pub storage_state_url: Option<Option<StorageStateUrl>>,
371 #[serde(default, deserialize_with = "double_option")]
373 pub storage_state: Option<Option<StorageState>>,
374 #[serde(default)]
375 pub proxy: Option<ResolvedProxyConfig>,
376}
377
378#[derive(Debug, Clone, Deserialize)]
380#[serde(rename_all = "camelCase")]
381pub(crate) struct ReplayUrlResponse {
382 #[serde(default)]
383 pub url: Option<String>,
384 #[serde(default)]
385 pub expires_in_seconds: Option<u64>,
386 #[serde(default)]
387 pub content_encoding: Option<String>,
388}
389
390#[derive(Debug, Clone, Deserialize)]
392#[serde(rename_all = "camelCase")]
393pub(crate) struct ProfileSaveResponse {
394 #[serde(default)]
395 pub version: Option<u64>,
396 #[serde(default)]
397 pub size_bytes: Option<u64>,
398}
399
400fn double_option<'de, T, D>(de: D) -> Result<Option<Option<T>>, D::Error>
403where
404 T: serde::Deserialize<'de>,
405 D: Deserializer<'de>,
406{
407 serde::Deserialize::deserialize(de).map(Some)
408}