firefox_webdriver/protocol/
request.rs1use serde::ser::{SerializeMap, Serializer};
13use serde::{Deserialize, Serialize};
14use serde_json::Value;
15
16use crate::error::{Error, Result};
17use crate::identifiers::{FrameId, RequestId, TabId};
18
19use super::Command;
20
21#[derive(Debug, Clone)]
39pub struct Request {
40 pub id: RequestId,
42
43 pub tab_id: TabId,
45
46 pub frame_id: FrameId,
48
49 pub command: Command,
51}
52
53impl Serialize for Request {
54 fn serialize<S: Serializer>(
55 &self,
56 serializer: S,
57 ) -> std::result::Result<S::Ok, S::Error> {
58 let command_value =
61 serde_json::to_value(&self.command).map_err(serde::ser::Error::custom)?;
62 let mut map = serializer.serialize_map(None)?;
63 map.serialize_entry("id", &self.id)?;
64 map.serialize_entry("tabId", &self.tab_id)?;
65 map.serialize_entry("frameId", &self.frame_id)?;
66 if let serde_json::Value::Object(obj) = command_value {
68 for (k, v) in obj {
69 map.serialize_entry(&k, &v)?;
70 }
71 }
72 map.end()
73 }
74}
75
76impl Request {
77 #[inline]
79 #[must_use]
80 pub fn new(tab_id: TabId, frame_id: FrameId, command: Command) -> Self {
81 Self {
82 id: RequestId::generate(),
83 tab_id,
84 frame_id,
85 command,
86 }
87 }
88
89 #[inline]
91 #[must_use]
92 pub fn with_id(id: RequestId, tab_id: TabId, frame_id: FrameId, command: Command) -> Self {
93 Self {
94 id,
95 tab_id,
96 frame_id,
97 command,
98 }
99 }
100}
101
102#[derive(Debug, Clone, Deserialize)]
129pub struct Response {
130 pub id: RequestId,
132
133 #[serde(rename = "type")]
135 pub response_type: ResponseType,
136
137 #[serde(default)]
139 pub result: Option<Value>,
140
141 #[serde(default)]
143 pub error: Option<String>,
144
145 #[serde(default)]
147 pub message: Option<String>,
148}
149
150impl Response {
151 #[inline]
153 #[must_use]
154 pub fn is_success(&self) -> bool {
155 self.response_type == ResponseType::Success
156 }
157
158 #[inline]
160 #[must_use]
161 pub fn is_error(&self) -> bool {
162 self.response_type == ResponseType::Error
163 }
164
165 pub fn into_result(self) -> Result<Value> {
171 match self.response_type {
172 ResponseType::Success => Ok(self.result.unwrap_or(Value::Null)),
173 ResponseType::Error => {
174 let error_code = self.error.unwrap_or_else(|| "unknown error".to_string());
175 let message = self.message.unwrap_or_else(|| error_code.clone());
176 Err(Error::protocol(message))
177 }
178 }
179 }
180
181 #[inline]
185 #[must_use]
186 pub fn get_str(&self, key: &str) -> Option<&str> {
187 self.result
188 .as_ref()
189 .and_then(|v| v.get(key))
190 .and_then(|v| v.as_str())
191 }
192
193 #[inline]
197 #[must_use]
198 pub fn get_string(&self, key: &str) -> String {
199 self.get_str(key).unwrap_or_default().to_string()
200 }
201
202 #[inline]
206 #[must_use]
207 pub fn get_u64(&self, key: &str) -> u64 {
208 self.result
209 .as_ref()
210 .and_then(|v| v.get(key))
211 .and_then(|v| v.as_u64())
212 .unwrap_or_default()
213 }
214
215 #[inline]
219 #[must_use]
220 pub fn get_bool(&self, key: &str) -> bool {
221 self.result
222 .as_ref()
223 .and_then(|v| v.get(key))
224 .and_then(|v| v.as_bool())
225 .unwrap_or_default()
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
235#[serde(rename_all = "lowercase")]
236pub enum ResponseType {
237 Success,
239 Error,
241}
242
243#[cfg(test)]
248mod tests {
249 use super::*;
250 use crate::protocol::BrowsingContextCommand;
251
252 #[test]
253 fn test_request_serialization() {
254 let tab_id = TabId::new(1).expect("valid tab id");
255 let frame_id = FrameId::main();
256 let command = Command::BrowsingContext(BrowsingContextCommand::Navigate {
257 url: "https://example.com".to_string(),
258 });
259
260 let request = Request::new(tab_id, frame_id, command);
261 let json = serde_json::to_string(&request).expect("serialize");
262
263 assert!(json.contains("browsingContext.navigate"));
264 assert!(json.contains("tabId"));
265 assert!(json.contains("frameId"));
266 }
267
268 #[test]
269 fn test_request_with_id() {
270 let id = RequestId::generate();
271 let tab_id = TabId::new(1).expect("valid tab id");
272 let frame_id = FrameId::main();
273 let command = Command::BrowsingContext(BrowsingContextCommand::GetTitle);
274
275 let request = Request::with_id(id, tab_id, frame_id, command);
276 assert_eq!(request.id, id);
277 }
278
279 #[test]
280 fn test_success_response() {
281 let json_str = r#"{
282 "id": "550e8400-e29b-41d4-a716-446655440000",
283 "type": "success",
284 "result": {"title": "Example"}
285 }"#;
286
287 let response: Response = serde_json::from_str(json_str).expect("parse");
288 assert!(response.is_success());
289 assert!(!response.is_error());
290 assert_eq!(response.get_string("title"), "Example");
291 }
292
293 #[test]
294 fn test_error_response() {
295 let json_str = r#"{
296 "id": "550e8400-e29b-41d4-a716-446655440000",
297 "type": "error",
298 "error": "no such element",
299 "message": "Element not found"
300 }"#;
301
302 let response: Response = serde_json::from_str(json_str).expect("parse");
303 assert!(response.is_error());
304 assert!(!response.is_success());
305 assert_eq!(response.error, Some("no such element".to_string()));
306 }
307
308 #[test]
309 fn test_into_result_success() {
310 let json_str = r#"{
311 "id": "550e8400-e29b-41d4-a716-446655440000",
312 "type": "success",
313 "result": {"value": 42}
314 }"#;
315
316 let response: Response = serde_json::from_str(json_str).expect("parse");
317 let result = response.into_result().expect("should succeed");
318 assert_eq!(result.get("value").and_then(|v| v.as_u64()), Some(42));
319 }
320
321 #[test]
322 fn test_into_result_error() {
323 let json_str = r#"{
324 "id": "550e8400-e29b-41d4-a716-446655440000",
325 "type": "error",
326 "error": "timeout",
327 "message": "Operation timed out"
328 }"#;
329
330 let response: Response = serde_json::from_str(json_str).expect("parse");
331 let result = response.into_result();
332 assert!(result.is_err());
333 }
334
335 #[test]
336 fn test_response_get_helpers() {
337 let json_str = r#"{
338 "id": "550e8400-e29b-41d4-a716-446655440000",
339 "type": "success",
340 "result": {
341 "name": "test",
342 "count": 42,
343 "enabled": true
344 }
345 }"#;
346
347 let response: Response = serde_json::from_str(json_str).expect("parse");
348 assert_eq!(response.get_string("name"), "test");
349 assert_eq!(response.get_u64("count"), 42);
350 assert!(response.get_bool("enabled"));
351
352 assert_eq!(response.get_string("missing"), "");
354 assert_eq!(response.get_u64("missing"), 0);
355 assert!(!response.get_bool("missing"));
356 }
357}