use std::fmt;
use busylib::model::busy::{
BusyBarSettings, BusyProfile, BusySnapshot, BusyTimerIntervalSettings, TimerSettings,
TimerState,
};
use serde::Serialize;
use crate::reporter::events::CliEvent;
use crate::reporter::events::fields::{Field, field, prefixed, write_fields};
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct BusySnapshotEvent(BusySnapshot);
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct BusyProfileEvent(BusyProfile);
impl BusySnapshotEvent {
pub fn new(snapshot: BusySnapshot) -> Self {
Self(snapshot)
}
}
impl BusyProfileEvent {
pub fn new(profile: BusyProfile) -> Self {
Self(profile)
}
}
impl From<BusySnapshotEvent> for CliEvent {
fn from(event: BusySnapshotEvent) -> Self {
CliEvent::BusySnapshot(Box::new(event))
}
}
impl From<BusyProfileEvent> for CliEvent {
fn from(event: BusyProfileEvent) -> Self {
CliEvent::BusyProfile(Box::new(event))
}
}
impl fmt::Display for BusySnapshotEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let snapshot = &self.0;
let mut fields = state_fields(&snapshot.snapshot.state);
fields.extend(prefixed(
"settings",
bar_settings_fields(&snapshot.snapshot.busy_bar_settings),
));
fields.push(field(
"snapshot timestamp ms",
snapshot.snapshot_timestamp_ms,
));
write_fields(f, &fields)
}
}
impl fmt::Display for BusyProfileEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let profile = &self.0;
let mut fields = vec![
field("id", &profile.id),
field("title", &profile.title),
field("sort order", profile.sort_order),
];
fields.extend(prefixed(
"timer",
timer_settings_fields(&profile.timer_settings),
));
fields.extend(prefixed(
"settings",
bar_settings_fields(&profile.busy_bar_settings),
));
fields.push(field("profile timestamp ms", profile.profile_timestamp_ms));
write_fields(f, &fields)
}
}
fn state_fields(state: &TimerState) -> Vec<Field> {
match state {
TimerState::NotStarted => vec![field("type", "NOT_STARTED")],
TimerState::Infinite { card_id, is_paused } => vec![
field("type", "INFINITE"),
field("card id", card_id),
field("paused", is_paused),
],
TimerState::Simple {
card_id,
time_left_ms,
is_paused,
} => vec![
field("type", "SIMPLE"),
field("card id", card_id),
field("time left ms", time_left_ms),
field("paused", is_paused),
],
TimerState::Interval {
card_id,
current_interval,
current_interval_time_total_ms,
current_interval_time_left_ms,
is_paused,
interval_settings,
} => {
let mut fields = vec![
field("type", "INTERVAL"),
field("card id", card_id),
field("current interval", current_interval),
field(
"current interval time total ms",
current_interval_time_total_ms,
),
field(
"current interval time left ms",
current_interval_time_left_ms,
),
field("paused", is_paused),
];
fields.extend(prefixed("interval", interval_fields(interval_settings)));
fields
}
}
}
fn timer_settings_fields(settings: &TimerSettings) -> Vec<Field> {
match settings {
TimerSettings::Infinite => vec![field("type", "INFINITE")],
TimerSettings::Simple { total_time_ms } => vec![
field("type", "SIMPLE"),
field("total time ms", total_time_ms),
],
TimerSettings::Interval {
interval_work_ms,
interval_rest_ms,
interval_work_cycles_count,
is_autostart_enabled,
} => vec![
field("type", "INTERVAL"),
field("work ms", interval_work_ms),
field("rest ms", interval_rest_ms),
field("work cycles count", interval_work_cycles_count),
field("autostart enabled", is_autostart_enabled),
],
}
}
fn interval_fields(settings: &BusyTimerIntervalSettings) -> Vec<Field> {
vec![
field("work ms", settings.interval_work_ms),
field("rest ms", settings.interval_rest_ms),
field("work cycles count", settings.interval_work_cycles_count),
field("autostart enabled", settings.is_autostart_enabled),
]
}
fn bar_settings_fields(settings: &BusyBarSettings) -> Vec<Field> {
vec![
field("theme", &settings.theme),
field("show work phase only", settings.show_work_phase_only),
field("trigger smart home", settings.trigger_smart_home),
]
}
#[cfg(test)]
mod tests {
use busylib::model::busy::Snapshot;
use super::*;
fn bar_settings() -> BusyBarSettings {
BusyBarSettings {
theme: "default".to_string(),
show_work_phase_only: false,
trigger_smart_home: true,
}
}
#[test]
fn renders_the_running_timer_state() {
let event = BusySnapshotEvent::new(BusySnapshot {
snapshot: Snapshot {
state: TimerState::Infinite {
card_id: "00000000-0000-0000-0000-000000000001".to_string(),
is_paused: true,
},
busy_bar_settings: bar_settings(),
},
snapshot_timestamp_ms: 1761582532251,
});
assert_eq!(
event.to_string(),
"type: INFINITE\n\
card id: 00000000-0000-0000-0000-000000000001\n\
paused: true\n\
settings theme: default\n\
settings show work phase only: false\n\
settings trigger smart home: true\n\
snapshot timestamp ms: 1761582532251"
);
}
#[test]
fn prefixes_the_timer_settings_of_a_profile() {
let event = BusyProfileEvent::new(BusyProfile {
id: "00000000-0000-0000-0000-000000000000".to_string(),
title: "BUSY".to_string(),
sort_order: 0,
timer_settings: TimerSettings::Simple {
total_time_ms: 1_500_000,
},
busy_bar_settings: bar_settings(),
profile_timestamp_ms: 1761582532251,
});
assert_eq!(
event.to_string(),
"id: 00000000-0000-0000-0000-000000000000\n\
title: BUSY\n\
sort order: 0\n\
timer type: SIMPLE\n\
timer total time ms: 1500000\n\
settings theme: default\n\
settings show work phase only: false\n\
settings trigger smart home: true\n\
profile timestamp ms: 1761582532251"
);
}
}