firefox_webdriver/protocol/
request.rs1use serde::{Deserialize, Serialize};
13use serde_json::Value;
14
15use crate::error::{Error, Result};
16use crate::identifiers::{FrameId, RequestId, TabId};
17
18use super::Command;
19
20#[derive(Debug, Clone, Serialize)]
38pub struct Request {
39 pub id: RequestId,
41
42 #[serde(rename = "tabId")]
44 pub tab_id: TabId,
45
46 #[serde(rename = "frameId")]
48 pub frame_id: FrameId,
49
50 #[serde(flatten)]
52 pub command: Command,
53}
54
55impl Request {
56 #[inline]
58 #[must_use]
59 pub fn new(tab_id: TabId, frame_id: FrameId, command: Command) -> Self {
60 Self {
61 id: RequestId::generate(),
62 tab_id,
63 frame_id,
64 command,
65 }
66 }
67
68 #[inline]
70 #[must_use]
71 pub fn with_id(id: RequestId, tab_id: TabId, frame_id: FrameId, command: Command) -> Self {
72 Self {
73 id,
74 tab_id,
75 frame_id,
76 command,
77 }
78 }
79}
80
81#[derive(Debug, Clone, Deserialize)]
108pub struct Response {
109 pub id: RequestId,
111
112 #[serde(rename = "type")]
114 pub response_type: ResponseType,
115
116 #[serde(default)]
118 pub result: Option<Value>,
119
120 #[serde(default)]
122 pub error: Option<String>,
123
124 #[serde(default)]
126 pub message: Option<String>,
127}
128
129impl Response {
130 #[inline]
132 #[must_use]
133 pub fn is_success(&self) -> bool {
134 self.response_type == ResponseType::Success
135 }
136
137 #[inline]
139 #[must_use]
140 pub fn is_error(&self) -> bool {
141 self.response_type == ResponseType::Error
142 }
143
144 pub fn into_result(self) -> Result<Value> {
150 match self.response_type {
151 ResponseType::Success => Ok(self.result.unwrap_or(Value::Null)),
152 ResponseType::Error => {
153 let error_code = self.error.unwrap_or_else(|| "unknown error".to_string());
154 let message = self.message.unwrap_or_else(|| error_code.clone());
155 Err(Error::protocol(message))
156 }
157 }
158 }
159
160 #[inline]
164 #[must_use]
165 pub fn get_string(&self, key: &str) -> String {
166 self.result
167 .as_ref()
168 .and_then(|v| v.get(key))
169 .and_then(|v| v.as_str())
170 .unwrap_or_default()
171 .to_string()
172 }
173
174 #[inline]
178 #[must_use]
179 pub fn get_u64(&self, key: &str) -> u64 {
180 self.result
181 .as_ref()
182 .and_then(|v| v.get(key))
183 .and_then(|v| v.as_u64())
184 .unwrap_or_default()
185 }
186
187 #[inline]
191 #[must_use]
192 pub fn get_bool(&self, key: &str) -> bool {
193 self.result
194 .as_ref()
195 .and_then(|v| v.get(key))
196 .and_then(|v| v.as_bool())
197 .unwrap_or_default()
198 }
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
207#[serde(rename_all = "lowercase")]
208pub enum ResponseType {
209 Success,
211 Error,
213}
214
215#[cfg(test)]
220mod tests {
221 use super::*;
222 use crate::protocol::BrowsingContextCommand;
223
224 #[test]
225 fn test_request_serialization() {
226 let tab_id = TabId::new(1).expect("valid tab id");
227 let frame_id = FrameId::main();
228 let command = Command::BrowsingContext(BrowsingContextCommand::Navigate {
229 url: "https://example.com".to_string(),
230 });
231
232 let request = Request::new(tab_id, frame_id, command);
233 let json = serde_json::to_string(&request).expect("serialize");
234
235 assert!(json.contains("browsingContext.navigate"));
236 assert!(json.contains("tabId"));
237 assert!(json.contains("frameId"));
238 }
239
240 #[test]
241 fn test_request_with_id() {
242 let id = RequestId::generate();
243 let tab_id = TabId::new(1).expect("valid tab id");
244 let frame_id = FrameId::main();
245 let command = Command::BrowsingContext(BrowsingContextCommand::GetTitle);
246
247 let request = Request::with_id(id, tab_id, frame_id, command);
248 assert_eq!(request.id, id);
249 }
250
251 #[test]
252 fn test_success_response() {
253 let json_str = r#"{
254 "id": "550e8400-e29b-41d4-a716-446655440000",
255 "type": "success",
256 "result": {"title": "Example"}
257 }"#;
258
259 let response: Response = serde_json::from_str(json_str).expect("parse");
260 assert!(response.is_success());
261 assert!(!response.is_error());
262 assert_eq!(response.get_string("title"), "Example");
263 }
264
265 #[test]
266 fn test_error_response() {
267 let json_str = r#"{
268 "id": "550e8400-e29b-41d4-a716-446655440000",
269 "type": "error",
270 "error": "no such element",
271 "message": "Element not found"
272 }"#;
273
274 let response: Response = serde_json::from_str(json_str).expect("parse");
275 assert!(response.is_error());
276 assert!(!response.is_success());
277 assert_eq!(response.error, Some("no such element".to_string()));
278 }
279
280 #[test]
281 fn test_into_result_success() {
282 let json_str = r#"{
283 "id": "550e8400-e29b-41d4-a716-446655440000",
284 "type": "success",
285 "result": {"value": 42}
286 }"#;
287
288 let response: Response = serde_json::from_str(json_str).expect("parse");
289 let result = response.into_result().expect("should succeed");
290 assert_eq!(result.get("value").and_then(|v| v.as_u64()), Some(42));
291 }
292
293 #[test]
294 fn test_into_result_error() {
295 let json_str = r#"{
296 "id": "550e8400-e29b-41d4-a716-446655440000",
297 "type": "error",
298 "error": "timeout",
299 "message": "Operation timed out"
300 }"#;
301
302 let response: Response = serde_json::from_str(json_str).expect("parse");
303 let result = response.into_result();
304 assert!(result.is_err());
305 }
306
307 #[test]
308 fn test_response_get_helpers() {
309 let json_str = r#"{
310 "id": "550e8400-e29b-41d4-a716-446655440000",
311 "type": "success",
312 "result": {
313 "name": "test",
314 "count": 42,
315 "enabled": true
316 }
317 }"#;
318
319 let response: Response = serde_json::from_str(json_str).expect("parse");
320 assert_eq!(response.get_string("name"), "test");
321 assert_eq!(response.get_u64("count"), 42);
322 assert!(response.get_bool("enabled"));
323
324 assert_eq!(response.get_string("missing"), "");
326 assert_eq!(response.get_u64("missing"), 0);
327 assert!(!response.get_bool("missing"));
328 }
329}