bevy_websocket_adapter 0.1.5

Simple adapter to receive WebSocket messages in your bevy games as native Rust types.
Documentation
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::collections::HashMap;

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Enveloppe {
    #[serde(rename(serialize = "t", deserialize = "t"))]
    pub message_type: String,
    #[serde(rename(serialize = "d", deserialize = "d"))]
    pub payload: Box<serde_json::value::RawValue>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SendEnveloppe<T> {
    #[serde(rename(serialize = "t", deserialize = "t"))]
    pub message_type: String,
    #[serde(rename(serialize = "d", deserialize = "d"))]
    pub payload: T,
}

pub trait MessageType: Any + serde::de::DeserializeOwned + Send + Sync {
    fn message_type() -> &'static str;
}

type Df = Box<dyn Send + Fn(&serde_json::value::RawValue) -> anyhow::Result<Box<dyn Any + Send>>>;

fn generate_deserialize_fn<T: Any>() -> Df
where
    T: serde::de::DeserializeOwned + Send,
{
    Box::new(|v: &serde_json::value::RawValue| Ok(Box::new(serde_json::from_str::<T>(v.get())?)))
}

#[derive(Default)]
pub struct GenericParser {
    tps: HashMap<String, Box<dyn Any + Send>>,
}

impl GenericParser {
    pub fn new() -> Self {
        Self {
            tps: HashMap::new(),
        }
    }

    pub fn insert_type<T: MessageType>(&mut self) {
        let tag = T::message_type();
        if self.tps.get(tag).is_some() {
            panic!("type '{}' already registered", tag);
        }
        self.tps
            .insert(tag.to_string(), Box::new(generate_deserialize_fn::<T>()));
    }

    pub fn parse_as_any(
        &self,
        tag: &str,
        dat: &serde_json::value::RawValue,
    ) -> anyhow::Result<Box<dyn Any + Send>> {
        match self.tps.get(tag) {
            Some(func) => {
                return func
                    .downcast_ref::<Df>()
                    .expect("failed to load downcast function")(dat);
            }
            None => anyhow::bail!("type '{}' not registered", tag),
        }
    }

    pub fn parse_enveloppe(&self, ev: &Enveloppe) -> anyhow::Result<Box<dyn Any + Send>> {
        self.parse_as_any(&ev.message_type, &ev.payload)
    }

    pub fn try_into_concrete_type<T: 'static>(d: Box<dyn Any + Send>) -> anyhow::Result<T> {
        match d.downcast::<T>() {
            Ok(r) => Ok(*r),
            Err(_) => anyhow::bail!("downcast type mismatch"),
        }
    }
}