Skip to content

Instantly share code, notes, and snippets.

@rtyler
Created June 6, 2020 22:01
Show Gist options
  • Save rtyler/20d6dabeac8208c18395829b611ce84c to your computer and use it in GitHub Desktop.
Save rtyler/20d6dabeac8208c18395829b611ce84c to your computer and use it in GitHub Desktop.
Playing around with syntax sugar around handling websocket messages
INFO otto_eventbus > Listening for WebSocket connections on 127.0.0.1:9311
DEBUG tungstenite::handshake::server > Server handshake done.
DEBUG otto_eventbus > Received: Ok(Text("{\"type\":\"ping\", \"value\" : {\"msg\":\"hi\"}}"))
INFO otto_eventbus > deser: Meow { ttype: "ping", value: Object({"msg": String("hi")}) }
DEBUG otto_eventbus > Handling: Ping { msg: "hi" }
DEBUG otto_eventbus > Received: Ok(Text("{\"type\":\"hello\",\"value\": null}"))
INFO otto_eventbus > deser: Meow { ttype: "hello", value: Null }
DEBUG otto_eventbus > Handling hello: Hello
/**
* Meow is the primary message envolope
*/
#[derive(Debug, Deserialize, Serialize)]
struct Meow {
#[serde(rename = "type")]
ttype: String,
value: serde_json::Value,
}
trait Handler: Send + Sync {
/**
* convert will take the given Value and attempt to convert it to the trait implementer's type
*
* If the conversion cannot be done properly, None will be returned
*/
fn convert(v: serde_json::Value) -> Option<Self> where Self: std::marker::Sized + serde::de::DeserializeOwned {
serde_json::from_value::<Self>(v).map_or(None, |res| Some(res))
}
/**
* handle must be implemented by the trait implementer and should
* do something novel with the value
*/
fn handle(v: serde_json::Value) -> Result<(), std::io::Error>;
}
#[derive(Debug, Deserialize, Serialize)]
struct Ping {
msg: String,
}
impl Handler for Ping {
fn handle(v: serde_json::Value) -> Result<(), std::io::Error> {
if let Some(ping) = Self::convert(v) {
debug!("Handling: {:?}", ping);
}
Ok(())
}
}
#[derive(Debug, Deserialize, Serialize)]
struct Hello;
impl Handler for Hello {
fn handle(v: serde_json::Value) -> Result<(), std::io::Error> {
if let Some(hello) = Self::convert(v) {
debug!("Handling hello: {:?}", hello);
}
Ok(())
}
}
lazy_static! {
static ref REGISTRY: Mutex<HashMap<String, Dispatch>> = {
let mut reg = HashMap::new();
Mutex::new(reg)
};
}
macro_rules! register_handler {
($($t:ty), * => $($e:expr), *) => {
$(
REGISTRY.lock().expect("Failed to unlock registry")
.insert($e.to_string(), Dispatch::new(|v| { <$t>::handle(v); }));
)*
}
}
struct Dispatch {
f: Box<dyn Fn(serde_json::Value) + Send + Sync>,
}
impl Dispatch {
fn new<F>(f: F) -> Self
where
F: Fn(serde_json::Value) + 'static + Send + Sync,
{
Self { f: Box::new(f) }
}
}
async fn handle_ws(mut c: Connection) -> Result<(), std::io::Error> {
register_handler!(Ping => "ping");
register_handler!(Hello => "hello");
while let Some(item) = c.stream.next().await {
debug!("Received: {:?}", item);
match item {
Ok(msg) => {
if let Ok(meow) = serde_json::from_str::<Meow>(msg.to_text().unwrap()) {
info!("deser: {:?}", meow);
if let Ok(registry) = REGISTRY.lock() {
if let Some(container) = registry.get(&meow.ttype) {
(container.f)(meow.value);
}
}
}
},
_ => {
},
}
}
Ok(())
}
@penso
Copy link

penso commented Feb 9, 2022

@rtyler I would have expected the lib to preparse the ping messages, to easily send pong back with the same payload. Or even send pong automatically...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment