use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ProtocolKind {
Command,
Query,
Event,
Dto,
}
pub trait ProtocolMeta {
fn name() -> &'static str;
fn kind() -> ProtocolKind;
}
pub trait Command: Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static {}
pub trait ProtocolQuery: Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static {}
pub trait Event: Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static {}
pub trait Dto: Serialize + for<'de> Deserialize<'de> + Clone + Send + Sync + 'static {}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct SpawnUnit {
unit_id: u64,
location_id: u64,
}
impl Command for SpawnUnit {}
impl ProtocolMeta for SpawnUnit {
fn name() -> &'static str {
"SpawnUnit"
}
fn kind() -> ProtocolKind {
ProtocolKind::Command
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct GetWorldSnapshot;
impl ProtocolQuery for GetWorldSnapshot {}
impl ProtocolMeta for GetWorldSnapshot {
fn name() -> &'static str {
"GetWorldSnapshot"
}
fn kind() -> ProtocolKind {
ProtocolKind::Query
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct UnitDestroyed {
unit_id: u64,
arrived_at: u64,
}
impl Event for UnitDestroyed {}
impl ProtocolMeta for UnitDestroyed {
fn name() -> &'static str {
"UnitDestroyed"
}
fn kind() -> ProtocolKind {
ProtocolKind::Event
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct WorldSnapshot {
unit_count: u32,
}
impl Dto for WorldSnapshot {}
impl ProtocolMeta for WorldSnapshot {
fn name() -> &'static str {
"WorldSnapshot"
}
fn kind() -> ProtocolKind {
ProtocolKind::Dto
}
}
#[test]
fn protocol_kind_variants() {
assert_ne!(ProtocolKind::Command, ProtocolKind::Query);
assert_ne!(ProtocolKind::Event, ProtocolKind::Dto);
}
#[test]
fn protocol_kind_serde_roundtrip() {
for kind in [
ProtocolKind::Command,
ProtocolKind::Query,
ProtocolKind::Event,
ProtocolKind::Dto,
] {
let json = serde_json::to_string(&kind).unwrap();
let back: ProtocolKind = serde_json::from_str(&json).unwrap();
assert_eq!(kind, back);
}
}
#[test]
fn protocol_meta_command() {
assert_eq!(SpawnUnit::name(), "SpawnUnit");
assert_eq!(SpawnUnit::kind(), ProtocolKind::Command);
}
#[test]
fn protocol_meta_query() {
assert_eq!(GetWorldSnapshot::name(), "GetWorldSnapshot");
assert_eq!(GetWorldSnapshot::kind(), ProtocolKind::Query);
}
#[test]
fn protocol_meta_event() {
assert_eq!(UnitDestroyed::name(), "UnitDestroyed");
assert_eq!(UnitDestroyed::kind(), ProtocolKind::Event);
}
#[test]
fn protocol_meta_dto() {
assert_eq!(WorldSnapshot::name(), "WorldSnapshot");
assert_eq!(WorldSnapshot::kind(), ProtocolKind::Dto);
}
#[test]
fn command_serde_roundtrip() {
let cmd = SpawnUnit {
unit_id: 1,
location_id: 42,
};
let json = serde_json::to_string(&cmd).unwrap();
let back: SpawnUnit = serde_json::from_str(&json).unwrap();
assert_eq!(cmd, back);
}
#[test]
fn query_serde_roundtrip() {
let q = GetWorldSnapshot;
let json = serde_json::to_string(&q).unwrap();
let back: GetWorldSnapshot = serde_json::from_str(&json).unwrap();
assert_eq!(q, back);
}
#[test]
fn event_serde_roundtrip() {
let evt = UnitDestroyed {
unit_id: 1,
arrived_at: 1000,
};
let json = serde_json::to_string(&evt).unwrap();
let back: UnitDestroyed = serde_json::from_str(&json).unwrap();
assert_eq!(evt, back);
}
#[test]
fn dto_serde_roundtrip() {
let dto = WorldSnapshot { unit_count: 5 };
let json = serde_json::to_string(&dto).unwrap();
let back: WorldSnapshot = serde_json::from_str(&json).unwrap();
assert_eq!(dto, back);
}
#[test]
fn component_and_command_no_conflict() {
use crate::component::Component;
#[derive(Debug, Serialize, Deserialize)]
struct Health {
hp: u32,
}
impl Component for Health {}
impl Command for Health {}
impl ProtocolMeta for Health {
fn name() -> &'static str {
"Health"
}
fn kind() -> ProtocolKind {
ProtocolKind::Command
}
}
let h = Health { hp: 100 };
assert_eq!(Health::kind(), ProtocolKind::Command);
let json = serde_json::to_string(&h).unwrap();
assert!(json.contains("100"));
}
}