/* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use serde::de::{
self , SeqAccess, Unexpected, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::{Map, Value};
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::fmt;
use crate ::error::MarionetteError;
use crate ::marionette;
use crate ::result::MarionetteResult;
use crate ::webdriver;
#[ derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[ serde(untagged)]
pub enum Command {
WebDriver(webdriver::Command),
Marionette(marionette::Command),
}
impl Command {
pub fn name(&
self ) -> String {
let (command_name, _) =
self .first_entry();
command_name
}
fn params(&
self ) -> Value {
let (_, params) =
self .first_entry();
params
}
fn first_entry(&
self ) -> (String, serde_json::Value) {
match serde_json::to_value(
self ).unwrap() {
Value::String(cmd) => (cmd, Value::Object(Map::new())),
Value::Object(items) => {
let mut iter = items.iter();
let (cmd, params) = iter.next().unwrap();
(cmd.to_string(), params.clone())
}
_ => unreachable!(),
}
}
}
#[ derive(Clone, Debug, PartialEq, Serialize_repr, Deserialize_repr)]
#[ repr(u8)]
enum MessageDirection {
Incoming =
0 ,
Outgoing =
1 ,
}
pub type MessageId = u32;
#[ derive(Debug, Clone, PartialEq)]
pub struct Request(
pub MessageId,
pub Command);
impl Request {
pub fn id(&
self ) -> MessageId {
self .
0
}
pub fn command(&
self ) -> &Command {
&
self .
1
}
pub fn params(&
self ) -> Value {
self .command().params()
}
}
impl Serialize
for Request {
fn serialize<S>(&
self , serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
(
MessageDirection::Incoming,
self .id(),
self .command().name(),
self .params(),
)
.serialize(serializer)
}
}
#[ derive(Debug, PartialEq)]
pub enum Response {
Result {
id: MessageId,
result: MarionetteResult,
},
Error {
id: MessageId,
error: MarionetteError,
},
}
impl Serialize
for Response {
fn serialize<S>(&
self , serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Response::Result { id, result } => {
(MessageDirection::Outgoing, id, Value::Null, &result).serialize(serializer)
}
Response::Error { id, error } => {
(MessageDirection::Outgoing, id, &error, Value::Null).serialize(serializer)
}
}
}
}
#[ derive(Debug, PartialEq, Serialize)]
#[ serde(untagged)]
pub enum Message {
Incoming(Request),
Outgoing(Response),
}
struct MessageVisitor;
impl <
'de> Visitor<' de>
for MessageVisitor {
type Value = Message;
fn expecting(&
self , formatter: &
mut fmt:
:Formatter) -> fmt::Result {
formatter.write_str("four-element array" )
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let direction = seq
.next_element::<MessageDirection>()?
.ok_or_else(|| de::Error::invalid_length(0 , &self ))?;
let id: MessageId = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1 , &self ))?;
let msg = match direction {
MessageDirection::Incoming => {
let name: String = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(2 , &self ))?;
let params: Value = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(3 , &self ))?;
let command = match params {
Value::Object(ref items) if !items.is_empty() => {
let command_to_params = {
let mut m = Map::new();
m.insert(name, params);
Value::Object(m)
};
serde_json::from_value(command_to_params).map_err(de::Error::custom)
}
Value::Object(_) | Value::Null => {
serde_json::from_value(Value::String(name)).map_err(de::Error::custom)
}
x => Err(de::Error::custom(format!("unknown params type: {}" , x))),
}?;
Message::Incoming(Request(id, command))
}
MessageDirection::Outgoing => {
let maybe_error: Option<MarionetteError> = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(2 , &self ))?;
let response = if let Some(error) = maybe_error {
seq.next_element::<Value>()?
.ok_or_else(|| de::Error::invalid_length(3 , &self ))?
.as_null()
.ok_or_else(|| de::Error::invalid_type(Unexpected::Unit, &self ))?;
Response::Error { id, error }
} else {
let result: MarionetteResult = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(3 , &self ))?;
Response::Result { id, result }
};
Message::Outgoing(response)
}
};
Ok(msg)
}
}
impl <'de> Deserialize<' de> for Message {
fn deserialize<D>(deserializer: D) -> Result<Self , D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_seq(MessageVisitor)
}
}
#[ cfg(test)]
mod tests {
use serde_json::json;
use super ::*;
use crate ::common::*;
use crate ::error::{ErrorKind, MarionetteError};
use crate ::test::assert_ser_de;
#[ test]
fn test_incoming() {
let json =
json!([0 , 42 , "WebDriver:FindElement" , {"using" : "css selector" , "value" : "value" }]);
let find_element = webdriver::Command::FindElement(webdriver::Locator {
element: None,
using: webdriver::Selector::Css,
value: "value" .into(),
});
let req = Request(42 , Command::WebDriver(find_element));
let msg = Message::Incoming(req);
assert_ser_de(&msg, json);
}
#[ test]
fn test_incoming_empty_params() {
let json = json!([0 , 42 , "WebDriver:GetTimeouts" , {}]);
let req = Request(42 , Command::WebDriver(webdriver::Command::GetTimeouts));
let msg = Message::Incoming(req);
assert_ser_de(&msg, json);
}
#[ test]
fn test_incoming_common_params() {
let json = json!([0 , 42 , "Marionette:AcceptConnections" , {"value" : false }]);
let params = BoolValue::new(false );
let req = Request(
42 ,
Command::Marionette(marionette::Command::AcceptConnections(params)),
);
let msg = Message::Incoming(req);
assert_ser_de(&msg, json);
}
#[ test]
fn test_incoming_params_derived() {
assert!(serde_json::from_value::<Message>(
json!([0 ,42 ,"WebDriver:FindElement" ,{"using" :"foo" ,"value" :"foo" }])
)
.is_err());
assert!(serde_json::from_value::<Message>(
json!([0 ,42 ,"Marionette:AcceptConnections" ,{"value" :"foo" }])
)
.is_err());
}
#[ test]
fn test_incoming_no_params() {
assert!(serde_json::from_value::<Message>(
json!([0 ,42 ,"WebDriver:GetTimeouts" ,{"value" :true }])
)
.is_err());
assert!(serde_json::from_value::<Message>(
json!([0 ,42 ,"Marionette:Context" ,{"value" :"foo" }])
)
.is_err());
assert!(serde_json::from_value::<Message>(
json!([0 ,42 ,"Marionette:GetScreenOrientation" ,{"value" :true }])
)
.is_err());
}
#[ test]
fn test_outgoing_result() {
let json = json!([1 , 42 , null, { "value" : null }]);
let result = MarionetteResult::Null;
let msg = Message::Outgoing(Response::Result { id: 42 , result });
assert_ser_de(&msg, json);
}
#[ test]
fn test_outgoing_error() {
let json =
json!([1 , 42 , {"error" : "no such element" , "message" : "" , "stacktrace" : "" }, null]);
let error = MarionetteError {
kind: ErrorKind::NoSuchElement,
message: "" .into(),
stack: "" .into(),
};
let msg = Message::Outgoing(Response::Error { id: 42 , error });
assert_ser_de(&msg, json);
}
#[ test]
fn test_invalid_type() {
assert!(
serde_json::from_value::<Message>(json!([2 , 42 , "WebDriver:GetTimeouts" , {}])).is_err()
);
assert!(serde_json::from_value::<Message>(json!([3 , 42 , "no such element" , {}])).is_err());
}
#[ test]
fn test_missing_fields() {
// all fields are required
assert!(
serde_json::from_value::<Message>(json!([2 , 42 , "WebDriver:GetTimeouts" ])).is_err()
);
assert!(serde_json::from_value::<Message>(json!([2 , 42 ])).is_err());
assert!(serde_json::from_value::<Message>(json!([2 ])).is_err());
assert!(serde_json::from_value::<Message>(json!([])).is_err());
}
#[ test]
fn test_unknown_command() {
assert!(serde_json::from_value::<Message>(json!([0 , 42 , "hooba" , {}])).is_err());
}
#[ test]
fn test_unknown_error() {
assert!(serde_json::from_value::<Message>(json!([1 , 42 , "flooba" , {}])).is_err());
}
#[ test]
fn test_message_id_bounds() {
let overflow = i64::from(std::u32::MAX) + 1 ;
let underflow = -1 ;
fn get_timeouts(message_id: i64) -> Value {
json!([0 , message_id, "WebDriver:GetTimeouts" , {}])
}
assert!(serde_json::from_value::<Message>(get_timeouts(overflow)).is_err());
assert!(serde_json::from_value::<Message>(get_timeouts(underflow)).is_err());
}
}
Messung V0.5 in Prozent C=78 H=92 G=84
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-18)
¤
*© Formatika GbR, Deutschland