//! Clock service: publishes a `Wire` on every change of the displayed //! value, and accepts `ClockKind` wires from modules that want it to switch //! which kind it publishes. //! //! The ticker knows nothing about the app's `Message` type; it produces //! protocol wires from `common` that core routes to subscribers. use std::collections::HashMap; use chrono::{DateTime, Local, Timelike}; use iced::futures::{channel::mpsc, SinkExt}; use iced::Subscription; use common::{ClockPayload, Endpoint, Inbox, Wire}; /// Route-key namespace owned by this service; keys are `"clock."`. pub const NAMESPACE: &str = "clock."; /// Drives the clock, publishing only the kind modules last asked for. #[derive(Default)] pub struct ClockTicker { last: DateTime, // want: ClockKind, } impl ClockTicker { pub fn new() -> Self { Self::default() } /// Subscription that reads inbound wires from `inbox` and publishes /// changes to every module subscribed to this service's route key. pub fn run(inbox: Inbox) -> Subscription { Subscription::run_with(inbox, |inbox| { let key = inbox.key(); // We dont need to rx any msgs from modules let mut _rx = inbox.take().expect("inbox receiver is taken once"); iced::stream::channel(0, move |mut sender: mpsc::Sender| async move { let mut clock = ClockTicker::new(); let mut interval = tokio::time::interval(std::time::Duration::from_secs(1)); tracing::info!(key, "clock service started"); loop { tokio::select! { _ = interval.tick() => { if let Some(payload) = clock.tick() { tracing::debug!(key, ?payload, "publishing clock"); let wire = Wire::topic(Endpoint::service(key), key, payload); if sender.send(wire).await.is_err() { tracing::warn!(key, "clock send failed; stopping service"); return; } } } } } }) }) } /// Latest value for the wanted kind, or `None` if unchanged. pub fn tick(&mut self) -> Option { let value = chrono::Local::now(); let meow = self.last; if meow.minute() != value.minute() { return Some(ClockPayload::Mins(value.format("%H:%M:%S").to_string())); } if meow.second() != value.second() { return Some(ClockPayload::Seconds(value.format("%H:%M:%S").to_string())); } None } }