Skip to main content

limit_cli/tools/browser/client_ext/
storage.rs

1//! Storage, cookies, network, and device operations
2
3use crate::tools::browser::executor::BrowserError;
4use crate::tools::browser::types::{Cookie, Request};
5use serde_json::Value as JsonValue;
6
7/// Storage, cookies, network, and device operations for browser client
8pub trait StorageExt {
9    /// Get all cookies
10    fn cookies(
11        &self,
12    ) -> impl std::future::Future<Output = Result<Vec<Cookie>, BrowserError>> + Send;
13
14    /// Set a cookie
15    fn cookies_set(
16        &self,
17        name: &str,
18        value: &str,
19    ) -> impl std::future::Future<Output = Result<(), BrowserError>> + Send;
20
21    /// Get storage value (local or session)
22    fn storage_get(
23        &self,
24        storage_type: &str,
25        key: Option<&str>,
26    ) -> impl std::future::Future<Output = Result<JsonValue, BrowserError>> + Send;
27
28    /// Set storage value (local or session)
29    fn storage_set(
30        &self,
31        storage_type: &str,
32        key: &str,
33        value: &str,
34    ) -> impl std::future::Future<Output = Result<(), BrowserError>> + Send;
35
36    /// Get network requests
37    fn network_requests(
38        &self,
39        filter: Option<&str>,
40    ) -> impl std::future::Future<Output = Result<Vec<Request>, BrowserError>> + Send;
41
42    /// Set viewport size
43    fn set_viewport(
44        &self,
45        width: u32,
46        height: u32,
47        scale: Option<f32>,
48    ) -> impl std::future::Future<Output = Result<(), BrowserError>> + Send;
49
50    /// Set device emulation
51    fn set_device(
52        &self,
53        name: &str,
54    ) -> impl std::future::Future<Output = Result<(), BrowserError>> + Send;
55
56    /// Set geolocation
57    fn set_geo(
58        &self,
59        latitude: f64,
60        longitude: f64,
61    ) -> impl std::future::Future<Output = Result<(), BrowserError>> + Send;
62}
63
64impl StorageExt for super::super::BrowserClient {
65    async fn cookies(&self) -> Result<Vec<Cookie>, BrowserError> {
66        let output = self.executor().execute(&["cookies"]).await?;
67
68        if output.success {
69            let cookies = output
70                .stdout
71                .lines()
72                .filter(|line| !line.is_empty())
73                .filter_map(|line| {
74                    let parts: Vec<&str> = line.splitn(2, '=').collect();
75                    if parts.len() == 2 {
76                        Some(Cookie {
77                            name: parts[0].to_string(),
78                            value: parts[1].to_string(),
79                        })
80                    } else {
81                        None
82                    }
83                })
84                .collect();
85            Ok(cookies)
86        } else {
87            Err(BrowserError::Other(format!(
88                "Failed to get cookies: {}",
89                output.stderr
90            )))
91        }
92    }
93
94    async fn cookies_set(&self, name: &str, value: &str) -> Result<(), BrowserError> {
95        if name.is_empty() {
96            return Err(BrowserError::InvalidArguments(
97                "Cookie name cannot be empty".to_string(),
98            ));
99        }
100
101        let output = self
102            .executor()
103            .execute(&["cookies", "set", name, value])
104            .await?;
105
106        if output.success {
107            Ok(())
108        } else {
109            Err(BrowserError::Other(format!(
110                "Failed to set cookie: {}",
111                output.stderr
112            )))
113        }
114    }
115
116    async fn storage_get(
117        &self,
118        storage_type: &str,
119        key: Option<&str>,
120    ) -> Result<JsonValue, BrowserError> {
121        let valid_types = ["local", "session"];
122        if !valid_types.contains(&storage_type) {
123            return Err(BrowserError::InvalidArguments(format!(
124                "Invalid storage type '{}'. Valid types: {}",
125                storage_type,
126                valid_types.join(", ")
127            )));
128        }
129
130        let output = if let Some(k) = key {
131            self.executor()
132                .execute(&["storage", storage_type, "get", k])
133                .await?
134        } else {
135            self.executor()
136                .execute(&["storage", storage_type, "get"])
137                .await?
138        };
139
140        if output.success {
141            let trimmed = output.stdout.trim();
142            if trimmed.is_empty() {
143                Ok(JsonValue::Null)
144            } else {
145                Ok(serde_json::from_str(trimmed)
146                    .unwrap_or_else(|_| JsonValue::String(trimmed.to_string())))
147            }
148        } else {
149            Err(BrowserError::Other(format!(
150                "Failed to get storage: {}",
151                output.stderr
152            )))
153        }
154    }
155
156    async fn storage_set(
157        &self,
158        storage_type: &str,
159        key: &str,
160        value: &str,
161    ) -> Result<(), BrowserError> {
162        let valid_types = ["local", "session"];
163        if !valid_types.contains(&storage_type) {
164            return Err(BrowserError::InvalidArguments(format!(
165                "Invalid storage type '{}'. Valid types: {}",
166                storage_type,
167                valid_types.join(", ")
168            )));
169        }
170
171        if key.is_empty() {
172            return Err(BrowserError::InvalidArguments(
173                "Key cannot be empty".to_string(),
174            ));
175        }
176
177        let output = self
178            .executor()
179            .execute(&["storage", storage_type, "set", key, value])
180            .await?;
181
182        if output.success {
183            Ok(())
184        } else {
185            Err(BrowserError::Other(format!(
186                "Failed to set storage: {}",
187                output.stderr
188            )))
189        }
190    }
191
192    async fn network_requests(&self, filter: Option<&str>) -> Result<Vec<Request>, BrowserError> {
193        let output = if let Some(f) = filter {
194            self.executor().execute(&["network", "requests", f]).await?
195        } else {
196            self.executor().execute(&["network", "requests"]).await?
197        };
198
199        if output.success {
200            let requests = output
201                .stdout
202                .lines()
203                .filter(|line| !line.is_empty())
204                .filter_map(|line| {
205                    let parts: Vec<&str> = line.splitn(2, ' ').collect();
206                    if parts.len() == 2 {
207                        Some(Request {
208                            method: parts[0].to_string(),
209                            url: parts[1].to_string(),
210                        })
211                    } else {
212                        None
213                    }
214                })
215                .collect();
216            Ok(requests)
217        } else {
218            Err(BrowserError::Other(format!(
219                "Failed to get network requests: {}",
220                output.stderr
221            )))
222        }
223    }
224
225    async fn set_viewport(
226        &self,
227        width: u32,
228        height: u32,
229        scale: Option<f32>,
230    ) -> Result<(), BrowserError> {
231        let w = width.to_string();
232        let h = height.to_string();
233
234        let output = if let Some(s) = scale {
235            let s_str = s.to_string();
236            self.executor()
237                .execute(&["set", "viewport", &w, &h, &s_str])
238                .await?
239        } else {
240            self.executor()
241                .execute(&["set", "viewport", &w, &h])
242                .await?
243        };
244
245        if output.success {
246            Ok(())
247        } else {
248            Err(BrowserError::Other(format!(
249                "Failed to set viewport: {}",
250                output.stderr
251            )))
252        }
253    }
254
255    async fn set_device(&self, name: &str) -> Result<(), BrowserError> {
256        if name.is_empty() {
257            return Err(BrowserError::InvalidArguments(
258                "Device name cannot be empty".to_string(),
259            ));
260        }
261
262        let output = self.executor().execute(&["set", "device", name]).await?;
263
264        if output.success {
265            Ok(())
266        } else {
267            Err(BrowserError::Other(format!(
268                "Failed to set device: {}",
269                output.stderr
270            )))
271        }
272    }
273
274    async fn set_geo(&self, latitude: f64, longitude: f64) -> Result<(), BrowserError> {
275        let lat = latitude.to_string();
276        let lng = longitude.to_string();
277
278        let output = self.executor().execute(&["set", "geo", &lat, &lng]).await?;
279
280        if output.success {
281            Ok(())
282        } else {
283            Err(BrowserError::Other(format!(
284                "Failed to set geolocation: {}",
285                output.stderr
286            )))
287        }
288    }
289}