Skip to content

Instantly share code, notes, and snippets.

@andreastt
Created July 5, 2019 20:26
Show Gist options
  • Save andreastt/f2bf8e5f5fc4adad73670c36cf7203f6 to your computer and use it in GitHub Desktop.
Save andreastt/f2bf8e5f5fc4adad73670c36cf7203f6 to your computer and use it in GitHub Desktop.
#![allow(dead_code)]
#![allow(unused_imports)]
use serde::de::{self, SeqAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::{json, Map, Value};
use std::borrow::Cow;
use std::fmt;
use std::marker::PhantomData;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
enum Selector {
#[serde(rename = "css selector")]
CSS,
XPath,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Locator {
using: Selector,
value: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
enum WebDriverCommand {
#[serde(rename = "WebDriver:FindElement")]
FindElement(Locator),
#[serde(rename = "WebDriver:GetTimeouts")]
GetTimeouts,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
enum Command {
WebDriver(WebDriverCommand),
}
impl Command {
pub fn name(&self) -> String {
let (command_name, _) = self.first_entry();
command_name
}
pub fn params(&self) -> Value {
let (_, params) = self.first_entry();
params
}
fn first_entry(&self) -> (String, serde_json::Value) {
let val = serde_json::to_value(&self).unwrap();
let items = val.as_object().unwrap();
let mut iter = items.iter();
let (command, params) = iter.next().unwrap();
(command.to_string(), params.clone())
}
}
type MessageId = i32;
#[derive(Debug, Clone, PartialEq, Deserialize)]
struct Request(MessageId, 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,
{
(
0,
self.id(),
self.command().name(),
self.params(),
)
.serialize(serializer)
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(untagged)]
enum Message {
Incoming(Request),
}
// TODO(ato):
// - turn the following into a SeqAccess
// - maybe try implement it on Request
// - figure out responses (result and error)
// (error will need a similar approach as this)
impl<'de> Deserialize<'de> for Message {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Debug, Deserialize)]
struct Mapping {
direction: u8,
id: MessageId,
name: String,
params: Value,
};
let Mapping {
direction,
id,
name,
params,
} = Mapping::deserialize(deserializer)?;
// map command name (i.e. "WebDriver:FindElement") to parameters
// so the serialisation can be automatically derived for Command
let map = {
let mut command_to_params = Map::new();
command_to_params.insert(name, params);
Value::Object(command_to_params)
};
let msg = match direction {
0 => {
let command = serde_json::from_value(map).unwrap();
Message::Incoming(Request(id, command))
}
1 => panic!("outoging"),
_ => panic!(),
};
Ok(msg)
}
}
fn main() {
let locator = Locator {
using: Selector::CSS,
value: "body".into(),
};
let find_element = WebDriverCommand::FindElement(locator);
let command = Command::WebDriver(find_element);
let request = Request(42, command);
let msg = Message::Incoming(request);
dbg!(serde_json::to_string(&msg).unwrap());
dbg!(&msg);
let actual: Message = serde_json::from_value(
json!([0,42,"WebDriver:FindElement",{"using":"css selector","value":"body"}]),
)
.unwrap();
assert_eq!(msg, actual);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment