use crate::{channel, message, user}; use axum::response::sse; use axum::response::sse::KeepAlive; use std::time::Duration; pub mod app; mod broadcaster; mod extract; pub mod repo; mod routes; mod sequence; pub use self::{ broadcaster::Broadcaster, routes::router, sequence::{Instant, Sequence, Sequenced}, }; #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] #[serde(tag = "type", rename_all = "snake_case")] pub enum Event { User(user::Event), Channel(channel::Event), Message(message::Event), } // Serialized representation is intended to look like the serialized representation of `Event`, // above - though heartbeat events contain only a type field and none of the other event gubbins. // They don't have to participate in sequence numbering, aren't generated from stored data, and // generally Are Weird. #[derive(serde::Serialize)] #[serde(tag = "type", rename_all = "snake_case")] pub enum Heartbeat { Heartbeat, } impl Sequenced for Event { fn instant(&self) -> Instant { match self { Self::User(event) => event.instant(), Self::Channel(event) => event.instant(), Self::Message(event) => event.instant(), } } } impl From for Event { fn from(event: user::Event) -> Self { Self::User(event) } } impl From for Event { fn from(event: channel::Event) -> Self { Self::Channel(event) } } impl From for Event { fn from(event: message::Event) -> Self { Self::Message(event) } } impl Heartbeat { // The following values are a first-rough-guess attempt to balance noticing connection problems // quickly with managing the (modest) costs of delivering and processing heartbeats. Feel // encouraged to tune them if you have a better idea on how to set them! // Advise clients to expect heartbeats this often pub const TIMEOUT: Duration = Duration::from_secs(20); // Actually send heartbeats this often; this is shorter to allow time for the heartbeat to // arrive before the advised deadline. pub const INTERVAL: Duration = Duration::from_secs(15); } impl TryFrom for sse::Event { type Error = serde_json::Error; fn try_from(heartbeat: Heartbeat) -> Result { let heartbeat = serde_json::to_string_pretty(&heartbeat)?; let heartbeat = sse::Event::default().data(heartbeat); Ok(heartbeat) } } impl TryFrom for sse::KeepAlive { type Error = >::Error; fn try_from(heartbeat: Heartbeat) -> Result { let event = heartbeat.try_into()?; let keep_alive = KeepAlive::new().interval(Heartbeat::INTERVAL).event(event); Ok(keep_alive) } }