use std::collections::HashMap; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use crate::i18n::{t, t_with_args}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct HeartbeatEntry { pub status: u8, pub time: String, pub msg: String, #[serde(default)] pub ping: Option, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct MonitorHeartbeats { pub monitor_id: u64, pub heartbeats: Vec, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct UptimeData { pub monitor_id: u64, pub period_hours: u32, pub uptime_percentage: f64, } impl UptimeData { pub fn get_perc_formated(&self) -> String { format!("{:.2}%", self.uptime_percentage * 100.00) } } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct HeartbeatResponse { #[serde(rename = "heartbeatList")] pub heartbeat_list_raw: HashMap>, #[serde(rename = "uptimeList")] pub uptime_list_raw: HashMap, #[serde(skip)] pub monitors: Vec, #[serde(skip)] pub uptime_data: Vec, } impl HeartbeatResponse { pub fn process(&mut self) -> Result<()> { for (monitor_id_str, heartbeats) in &self.heartbeat_list_raw { let monitor_id = monitor_id_str .parse::() .with_context(|| format!("{}", t("invalid-monitor-id")))?; self.monitors.push(MonitorHeartbeats { monitor_id, heartbeats: heartbeats.clone(), }); } for (key, percentage) in &self.uptime_list_raw { let parts: Vec<&str> = key.split('_').collect(); if parts.len() != 2 { let t_args: &mut HashMap<&str, std::string::String> = &mut HashMap::<&str, String>::new(); t_args.insert("key", key.to_string()); return Err(anyhow::anyhow!(t_with_args( "invalid-uptime-key-format", t_args ))); } let monitor_id = parts[0] .parse::() .with_context(|| format!("{}", t("invalid-monitor-id")))?; let period_hours = parts[1] .parse::() .with_context(|| format!("{}", t("invalid-period-hours")))?; self.uptime_data.push(UptimeData { monitor_id, period_hours, uptime_percentage: *percentage, }); } self.monitors.sort_by_key(|m| m.monitor_id); self.uptime_data .sort_by_key(|u| (u.monitor_id, u.period_hours)); Ok(()) } }