use bevy::prelude::*;
use bevy_event_bus::BusEvent;
use std::any::{Any, TypeId};
use std::collections::HashMap;
pub trait DecoderFn: Send + Sync + 'static {
fn decode(&self, data: &[u8]) -> Option<Box<dyn Any + Send + Sync>>;
fn event_type_id(&self) -> TypeId;
fn name(&self) -> &'static str;
}
type DecoderClosure<T> = dyn Fn(&[u8]) -> Option<T> + Send + Sync + 'static;
type BoxedDecoderClosure<T> = Box<DecoderClosure<T>>;
pub struct TypedDecoder<T: BusEvent + Message> {
decoder_fn: BoxedDecoderClosure<T>,
name: &'static str,
}
impl<T: BusEvent + Message> TypedDecoder<T> {
pub fn new<F>(decoder_fn: F, name: &'static str) -> Self
where
F: Fn(&[u8]) -> Option<T> + Send + Sync + 'static,
{
Self {
decoder_fn: Box::new(decoder_fn),
name,
}
}
pub fn json_decoder() -> Self {
Self::new(
|bytes| serde_json::from_slice::<T>(bytes).ok(),
std::any::type_name::<T>(),
)
}
}
impl<T: BusEvent + Message> DecoderFn for TypedDecoder<T> {
fn decode(&self, data: &[u8]) -> Option<Box<dyn Any + Send + Sync>> {
(self.decoder_fn)(data).map(|event| Box::new(event) as Box<dyn Any + Send + Sync>)
}
fn event_type_id(&self) -> TypeId {
TypeId::of::<T>()
}
fn name(&self) -> &'static str {
self.name
}
}
#[derive(Resource)]
pub struct DecoderRegistry {
topic_decoders: HashMap<String, Vec<Box<dyn DecoderFn>>>,
decoder_stats: HashMap<&'static str, DecoderStats>,
}
#[derive(Debug, Clone, Default)]
pub struct DecoderStats {
pub attempts: usize,
pub successes: usize,
pub failures: usize,
}
impl DecoderRegistry {
pub fn new() -> Self {
Self {
topic_decoders: HashMap::new(),
decoder_stats: HashMap::new(),
}
}
pub fn register_decoder<T: BusEvent + Message>(
&mut self,
topic: &str,
decoder: TypedDecoder<T>,
) {
let decoder_name = decoder.name();
let decoders = self.topic_decoders.entry(topic.to_string()).or_default();
decoders.push(Box::new(decoder));
bevy::log::debug!(
topic = %topic,
decoder = %decoder_name,
total_decoders = decoders.len(),
"Registered new decoder"
);
}
pub fn register_json_decoder<T: BusEvent + Message>(&mut self, topic: &str) {
self.register_decoder(topic, TypedDecoder::<T>::json_decoder());
}
pub fn get_decoders(&self, topic: &str) -> &[Box<dyn DecoderFn>] {
self.topic_decoders
.get(topic)
.map(|v| v.as_slice())
.unwrap_or(&[])
}
pub fn decode_all(&mut self, topic: &str, data: &[u8]) -> Vec<DecodedEvent> {
let mut results = Vec::new();
if let Some(decoders) = self.topic_decoders.get(topic) {
for decoder in decoders {
let decoder_name = decoder.name();
let stats = self.decoder_stats.entry(decoder_name).or_default();
stats.attempts += 1;
match decoder.decode(data) {
Some(event_any) => {
stats.successes += 1;
results.push(DecodedEvent {
event: event_any,
type_id: decoder.event_type_id(),
decoder_name,
});
bevy::log::trace!(
topic = %topic,
decoder = %decoder_name,
"Decoder succeeded"
);
}
None => {
stats.failures += 1;
bevy::log::trace!(
topic = %topic,
decoder = %decoder_name,
"Decoder failed"
);
}
}
}
}
results
}
pub fn get_stats(&self) -> &HashMap<&'static str, DecoderStats> {
&self.decoder_stats
}
pub fn topics(&self) -> Vec<String> {
self.topic_decoders.keys().cloned().collect()
}
pub fn decoder_count(&self, topic: &str) -> usize {
self.topic_decoders.get(topic).map(|v| v.len()).unwrap_or(0)
}
}
impl Default for DecoderRegistry {
fn default() -> Self {
Self::new()
}
}
pub struct DecodedEvent {
pub event: Box<dyn Any + Send + Sync>,
pub type_id: TypeId,
pub decoder_name: &'static str,
}
impl DecodedEvent {
pub fn downcast<T: 'static>(self) -> Result<T, Box<dyn Any + Send + Sync>> {
self.event.downcast::<T>().map(|boxed| *boxed)
}
pub fn is_type<T: 'static>(&self) -> bool {
self.type_id == TypeId::of::<T>()
}
pub fn as_any(&self) -> &(dyn Any + Send + Sync) {
self.event.as_ref()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Message)]
struct PlayerMove {
x: f32,
y: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Message)]
struct PlayerAttack {
target: String,
damage: u32,
}
#[test]
fn test_decoder_registry_basic() {
let mut registry = DecoderRegistry::new();
registry.register_json_decoder::<PlayerMove>("game_events");
registry.register_json_decoder::<PlayerAttack>("game_events");
assert_eq!(registry.decoder_count("game_events"), 2);
assert_eq!(registry.decoder_count("unknown_topic"), 0);
}
#[test]
fn test_multi_decoder_success() {
let mut registry = DecoderRegistry::new();
registry.register_json_decoder::<PlayerMove>("game_events");
let move_json = r#"{"x": 10.5, "y": 20.0}"#;
let results = registry.decode_all("game_events", move_json.as_bytes());
assert_eq!(
results.len(),
1,
"Expected 1 decoded result, got {}",
results.len()
);
let decoded_any = &results[0].event;
println!(
"Can downcast to PlayerMove: {}",
decoded_any.downcast_ref::<PlayerMove>().is_some()
);
if let Some(player_move) = decoded_any.downcast_ref::<PlayerMove>() {
println!("Decoded PlayerMove: {:?}", player_move);
}
println!(
"Expected TypeId: {:?}",
std::any::TypeId::of::<PlayerMove>()
);
println!("Actual TypeId: {:?}", results[0].type_id);
println!("Decoder name: {}", results[0].decoder_name);
let decoded_move = results
.into_iter()
.next()
.unwrap()
.downcast::<PlayerMove>()
.unwrap();
assert_eq!(decoded_move, PlayerMove { x: 10.5, y: 20.0 });
}
#[test]
fn test_multi_decoder_multiple_success() {
let mut registry = DecoderRegistry::new();
registry.register_decoder(
"test_topic",
TypedDecoder::new(
|_| Some(PlayerMove { x: 1.0, y: 2.0 }),
"custom_move_decoder",
),
);
registry.register_decoder(
"test_topic",
TypedDecoder::new(
|_| {
Some(PlayerAttack {
target: "test".into(),
damage: 100,
})
},
"custom_attack_decoder",
),
);
let results = registry.decode_all("test_topic", b"any data");
assert_eq!(results.len(), 2);
let move_result = results
.iter()
.find(|r| r.decoder_name == "custom_move_decoder")
.unwrap();
let attack_result = results
.iter()
.find(|r| r.decoder_name == "custom_attack_decoder")
.unwrap();
assert!(move_result.event.downcast_ref::<PlayerMove>().is_some());
assert!(attack_result.event.downcast_ref::<PlayerAttack>().is_some());
assert_eq!(move_result.decoder_name, "custom_move_decoder");
assert_eq!(attack_result.decoder_name, "custom_attack_decoder");
}
#[test]
fn test_decoder_failure_handling() {
let mut registry = DecoderRegistry::new();
registry.register_json_decoder::<PlayerMove>("game_events");
let invalid_json = b"not json at all";
let results = registry.decode_all("game_events", invalid_json);
assert_eq!(results.len(), 0);
let stats = registry.get_stats();
let decoder_stats = stats.get(std::any::type_name::<PlayerMove>()).unwrap();
assert_eq!(decoder_stats.attempts, 1);
assert_eq!(decoder_stats.failures, 1);
assert_eq!(decoder_stats.successes, 0);
}
}