use std::{ cell::RefCell, collections::HashMap, hash::{Hash, Hasher}, rc::Rc, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::prelude::*; const MESSAGE_MAGIC: u64 = 0x1234123412341234; /// Metadata about a message type. pub trait MessageParams: 'static { const NAME: &'static str; const ENCRYPTED: bool = true; const SIGNED: bool = false; /// Helper wrapper around the [`Message::build`] function; you don't need to implement this, /// it's only here for convenience at message construction sites. fn into_message(self) -> Message where Self: Serialize, Self: Sized, { Message::build(self) } } #[derive(Clone, Copy, Debug)] pub(crate) struct SavedMessageParams { #[allow(unused)] pub(crate) name: &'static str, pub(crate) encrypted: bool, pub(crate) signed: bool, } impl SavedMessageParams { fn save() -> Self { Self { name: M::NAME, encrypted: M::ENCRYPTED, signed: M::SIGNED, } } } fn message_type() -> u16 { let mut hasher = std::collections::hash_map::DefaultHasher::new(); M::NAME.hash(&mut hasher); (hasher.finish() & 0xffff) as u16 } #[derive(Serialize, Deserialize, Debug)] pub struct MessageContent { pub(crate) ty: u16, pub(crate) data: Vec, } #[derive(Serialize, Deserialize, Debug)] pub struct Message { magic: u64, pub(crate) crypto_header: fleck_core::crypto::PacketHeader, pub(crate) content: MessageContent, #[serde(skip_serializing)] #[serde(skip_deserializing)] pub(crate) saved_params: Option, #[serde(skip_serializing)] #[serde(skip_deserializing)] pub(crate) peer: Option, #[serde(skip_serializing)] #[serde(skip_deserializing)] pub(crate) node: Option>, } #[derive(Default)] pub struct Metadata { pub peer: Option, pub node: Option>, } impl Message { pub fn build(from: M) -> Self { Self { magic: MESSAGE_MAGIC, crypto_header: Default::default(), content: MessageContent { ty: message_type::(), data: bincode::serialize(&from).expect("couldn't serialize message"), }, saved_params: Some(SavedMessageParams::save::()), peer: None, node: None, } } fn metadata(&self) -> Metadata { Metadata { peer: self.peer.clone(), node: None, } } pub fn with_peer(mut self, peer: fleck_core::peer::Peer) -> Self { self.peer = Some(peer); self } pub fn with_node(mut self, node: Rc) -> Self { if self.peer.is_none() { self.peer = node.peer(); } self.node = Some(node); self } } impl From for Message { fn from(m: T) -> Self { Self::build(m) } } #[doc(hidden)] pub struct MessageChannelTag; /// Probably the most-commonly used family of channels in `fleck_core`: incoming message /// distribution! /// /// A minimal example of a service that listens for a packet on this channel might be: /// /// ```rust /// # use fleck::prelude::*; /// # use std::rc::Rc; /// # #[derive(Default, Debug, serde::Serialize, serde::Deserialize)] /// # struct ExampleMessageType {} /// #[derive(Default)] /// struct ExampleService; /// impl ExampleService { /// fn handle(&self, msg: (fleck_core::msg::Metadata, ExampleMessageType)) { /// // TODO: process the message /// } /// } /// impl DefaultService for ExampleService { /// fn setup(self: &Rc, api: std::rc::Rc) { /// api.channel::>().sub_eat(self, Self::handle); /// } /// } /// /// ``` pub type MessageChannel = (MessageChannelTag, (Metadata, M)); type Deserializer = dyn Fn(&Message); pub struct MessageService { api: Rc, deser: RefCell>>, } impl Service for MessageService { fn new(api: Rc) -> Self { Self { api, deser: Default::default(), } } fn setup(self: &Rc) { self.api .channel::() .sub_opt(fleck_core::ReceiveOrder::Parse, self, Self::parse); } } impl MessageService { pub fn add_message_type( &self, ) { let derived_typeid = message_type::(); let api = self.api.clone(); self.deser.borrow_mut().insert( derived_typeid, Box::new( move |message| match bincode::deserialize::(&message.content.data) { Ok(content) => { log::trace!("packet deserialized: {:?}", content); api.queue::>((message.metadata(), content)); }, Err(_) => { log::info!("Packet failed deserialization step!"); }, }, ), ); self.api.create_channel::>(); } fn parse(&self, msg: Message) -> Option { // try deserializing (self.deser.borrow().get(&msg.content.ty)?)(&msg); None } }