limit_cli/tools/browser/client_ext/
storage.rs1use crate::tools::browser::executor::BrowserError;
4use crate::tools::browser::types::{Cookie, Request};
5use serde_json::Value as JsonValue;
6
7pub trait StorageExt {
9 fn cookies(
11 &self,
12 ) -> impl std::future::Future<Output = Result<Vec<Cookie>, BrowserError>> + Send;
13
14 fn cookies_set(
16 &self,
17 name: &str,
18 value: &str,
19 ) -> impl std::future::Future<Output = Result<(), BrowserError>> + Send;
20
21 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 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 fn network_requests(
38 &self,
39 filter: Option<&str>,
40 ) -> impl std::future::Future<Output = Result<Vec<Request>, BrowserError>> + Send;
41
42 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 fn set_device(
52 &self,
53 name: &str,
54 ) -> impl std::future::Future<Output = Result<(), BrowserError>> + Send;
55
56 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}