use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct HostPerf {
pub pc_id: String,
pub at: chrono::DateTime<chrono::Utc>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpu_pct: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpu_count: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mem_used_bytes: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mem_total_bytes: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub swap_used_bytes: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub swap_total_bytes: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub disk_read_bytes_per_sec: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub disk_written_bytes_per_sec: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub net_rx_bytes_per_sec: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub net_tx_bytes_per_sec: Option<f64>,
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::TimeZone;
#[test]
fn host_perf_round_trips_through_json() {
let s = HostPerf {
pc_id: "minipc".into(),
at: chrono::Utc.with_ymd_and_hms(2026, 5, 24, 0, 0, 0).unwrap(),
cpu_pct: Some(12.5),
cpu_count: Some(8),
mem_used_bytes: Some(8_000_000_000),
mem_total_bytes: Some(16_000_000_000),
swap_used_bytes: Some(0),
swap_total_bytes: Some(4_000_000_000),
disk_read_bytes_per_sec: Some(1024.0 * 1024.0),
disk_written_bytes_per_sec: Some(512.0 * 1024.0),
net_rx_bytes_per_sec: Some(2048.0),
net_tx_bytes_per_sec: Some(1024.0),
};
let json = serde_json::to_string(&s).unwrap();
let back: HostPerf = serde_json::from_str(&json).unwrap();
assert_eq!(back.pc_id, s.pc_id);
assert_eq!(back.at, s.at);
assert_eq!(back.cpu_pct, s.cpu_pct);
assert_eq!(back.cpu_count, s.cpu_count);
assert_eq!(back.mem_used_bytes, s.mem_used_bytes);
assert_eq!(back.mem_total_bytes, s.mem_total_bytes);
assert_eq!(back.swap_used_bytes, s.swap_used_bytes);
assert_eq!(back.swap_total_bytes, s.swap_total_bytes);
assert_eq!(back.disk_read_bytes_per_sec, s.disk_read_bytes_per_sec);
assert_eq!(
back.disk_written_bytes_per_sec,
s.disk_written_bytes_per_sec
);
assert_eq!(back.net_rx_bytes_per_sec, s.net_rx_bytes_per_sec);
assert_eq!(back.net_tx_bytes_per_sec, s.net_tx_bytes_per_sec);
}
#[test]
fn host_perf_with_all_optional_fields_omitted_still_decodes() {
let json = r#"{"pc_id":"x","at":"2026-05-24T00:00:00Z"}"#;
let s: HostPerf = serde_json::from_str(json).unwrap();
assert_eq!(s.pc_id, "x");
assert!(s.cpu_pct.is_none());
assert!(s.mem_used_bytes.is_none());
assert!(s.disk_read_bytes_per_sec.is_none());
assert!(s.net_rx_bytes_per_sec.is_none());
}
}