use crate::error::TcpServerError; use domain::{BroadcastPort, DisplayHint, Layout, ThemeConfig, WidgetId, WidgetState}; use protocol::{ServerMessage, WidgetDescriptor, WireColor, WireLayoutNode, WireTheme, encode}; use tokio::sync::broadcast; pub struct TcpBroadcaster { tx: broadcast::Sender>, } impl TcpBroadcaster { pub fn new(capacity: usize) -> Self { let (tx, _) = broadcast::channel(capacity); Self { tx } } pub fn subscribe(&self) -> broadcast::Receiver> { self.tx.subscribe() } fn send_frame(&self, frame: Vec) -> Result<(), TcpServerError> { let _ = self.tx.send(frame); Ok(()) } } impl BroadcastPort for TcpBroadcaster { type Error = TcpServerError; async fn push_screen_update( &self, layout: &Layout, widgets: &[(WidgetId, DisplayHint, WidgetState)], ) -> Result<(), Self::Error> { let wire_layout: WireLayoutNode = (&layout.root).into(); let wire_widgets: Vec = widgets .iter() .map(|(id, hint, state)| WidgetDescriptor { id: *id, display_hint: hint.into(), state: state.into(), }) .collect(); let msg = ServerMessage::ScreenUpdate { layout: wire_layout, widgets: wire_widgets, }; let frame = encode(&msg).map_err(TcpServerError::Encode)?; self.send_frame(frame) } async fn push_data_update( &self, updates: &[(WidgetId, DisplayHint, WidgetState)], ) -> Result<(), Self::Error> { let wire_widgets: Vec = updates .iter() .map(|(id, hint, state)| WidgetDescriptor { id: *id, display_hint: hint.into(), state: state.into(), }) .collect(); let msg = ServerMessage::DataUpdate { widgets: wire_widgets, }; let frame = encode(&msg).map_err(TcpServerError::Encode)?; self.send_frame(frame) } async fn push_theme_update(&self, theme: &ThemeConfig) -> Result<(), Self::Error> { let wire_theme = domain_theme_to_wire(theme); let msg = ServerMessage::ThemeUpdate { theme: wire_theme }; let frame = encode(&msg).map_err(TcpServerError::Encode)?; self.send_frame(frame) } } pub(crate) fn domain_theme_to_wire(t: &ThemeConfig) -> WireTheme { let c = |c: &domain::ThemeColor| WireColor { r: c.r, g: c.g, b: c.b, }; WireTheme { primary: c(&t.primary), secondary: c(&t.secondary), accent: c(&t.accent), text: c(&t.text), background: c(&t.background), } }