Files
barbar/crates/services/src/datetime.rs
T
2026-09-14 00:55:25 +01:00

74 lines
2.7 KiB
Rust

//! 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.<kind>"`.
pub const NAMESPACE: &str = "clock.";
/// Drives the clock, publishing only the kind modules last asked for.
#[derive(Default)]
pub struct ClockTicker {
last: DateTime<Local>,
// 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<Wire> {
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<Wire>| 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<ClockPayload> {
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
}
}