gregg_protocol/snapshot.rs
1//! Snapshot and identity wire types.
2
3use serde::{Deserialize, Serialize};
4
5use crate::ValidationViolation;
6
7/// Top-level daemon snapshot returned by the status endpoint.
8///
9/// Every numeric field uses raw units. CPU and memory percentages are reported
10/// in the closed interval `0.0..=100.0`. Bytes are unsigned 64-bit counts.
11/// `observed_at_unix_ms` is the Unix epoch in milliseconds at which the
12/// underlying counters were sampled.
13///
14/// CPU percentage values are derived from sampling-interval deltas, not from
15/// instantaneous single reads, so a freshly started daemon may legitimately
16/// report a snapshot whose CPU usage is still unknown. Such snapshots surface
17/// through the [`HealthResponse`](crate::HealthResponse) instead of through
18/// this endpoint.
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20#[serde(rename_all = "snake_case")]
21pub struct StatusSnapshot {
22 /// Schema major version. Must equal
23 /// [`crate::SCHEMA_VERSION_V1`] for this endpoint.
24 pub schema_version: u16,
25 /// Unix epoch in milliseconds at which the snapshot was produced.
26 pub observed_at_unix_ms: u64,
27 /// Sampling cadence in milliseconds used to derive percentage metrics.
28 pub sample_interval_ms: u64,
29 /// Per-metric capability flags.
30 pub capabilities: MetricCapabilities,
31 /// Stable identity fields reported separately so clients can degrade by
32 /// width priority.
33 pub system: SystemIdentity,
34 /// CPU utilization, with optional Linux aggregate I/O wait.
35 pub cpu: CpuMetrics,
36 /// One-, five-, and fifteen-minute load averages.
37 pub load: LoadAverage,
38 /// Physical memory utilization.
39 pub memory: MemoryMetrics,
40 /// Swap utilization.
41 pub swap: SwapMetrics,
42}
43
44/// Per-metric capability flags.
45///
46/// A `false` flag means the metric is **unsupported on this platform**.
47/// Servers report `None` for unsupported values; clients render those values
48/// as absent rather than as zero.
49///
50/// A `true` flag means the metric is supported; the corresponding value must
51/// still be present in a `Ready` snapshot but may be absent during warmup.
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
53#[serde(rename_all = "snake_case")]
54pub struct MetricCapabilities {
55 /// Whether aggregate CPU I/O wait is reported.
56 ///
57 /// `false` on macOS, where no equivalent accounting state exists.
58 /// `true` on Linux.
59 pub cpu_iowait: bool,
60}
61
62/// Stable identity fields. Each field is transported separately so the TUI
63/// can degrade by width priority without parsing a combined string.
64#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65#[serde(rename_all = "snake_case")]
66pub struct SystemIdentity {
67 /// User-facing system name (typically a configured alias).
68 pub name: String,
69 /// Network hostname as reported by the operating system.
70 pub hostname: String,
71 /// Operating-system family name (e.g. `"linux"`, `"macos"`).
72 pub os_name: String,
73 /// Operating-system version string.
74 pub os_version: String,
75 /// Kernel name (e.g. `"Linux"`, `"Darwin"`).
76 pub kernel_name: String,
77 /// Kernel release string.
78 pub kernel_release: String,
79 /// Target architecture (e.g. `"x86_64"`, `"aarch64"`).
80 pub architecture: String,
81}
82
83/// CPU utilization snapshot.
84///
85/// `usage_pct` is total CPU busy over the most recent sampling interval.
86/// `iowait_pct` is the aggregate CPU I/O-wait time over the same interval;
87/// it is `Some(_)` only when [`MetricCapabilities::cpu_iowait`] is `true`.
88#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
89#[serde(rename_all = "snake_case")]
90pub struct CpuMetrics {
91 /// Number of logical CPU cores available to the kernel.
92 pub logical_cores: u32,
93 /// Total CPU busy percentage, `0.0..=100.0`, derived from delta samples.
94 pub usage_pct: f32,
95 /// Aggregate CPU I/O-wait percentage, `0.0..=100.0`. `None` when the
96 /// platform does not expose this state.
97 pub iowait_pct: Option<f32>,
98}
99
100/// One-, five-, and fifteen-minute load averages as reported by the
101/// operating system.
102#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
103#[serde(rename_all = "snake_case")]
104pub struct LoadAverage {
105 /// One-minute load average.
106 pub one: f32,
107 /// Five-minute load average.
108 pub five: f32,
109 /// Fifteen-minute load average.
110 pub fifteen: f32,
111}
112
113/// Physical memory utilization.
114///
115/// `usage_pct` is computed as `100.0 * used_bytes / total_bytes` and clamped
116/// to the closed interval `0.0..=100.0`.
117#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
118#[serde(rename_all = "snake_case")]
119pub struct MemoryMetrics {
120 /// Used physical memory in bytes. Never exceeds `total_bytes`.
121 pub used_bytes: u64,
122 /// Total physical memory in bytes.
123 pub total_bytes: u64,
124 /// Memory utilization percentage, `0.0..=100.0`.
125 pub usage_pct: f32,
126}
127
128/// Swap utilization.
129///
130/// When `total_bytes` is zero, `usage_pct` is `0.0` rather than `NaN`.
131#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
132#[serde(rename_all = "snake_case")]
133pub struct SwapMetrics {
134 /// Used swap in bytes. Never exceeds `total_bytes`.
135 pub used_bytes: u64,
136 /// Total swap in bytes.
137 pub total_bytes: u64,
138 /// Swap utilization percentage, `0.0..=100.0`. Zero when
139 /// `total_bytes == 0`.
140 pub usage_pct: f32,
141}
142
143impl StatusSnapshot {
144 /// Validate that every field satisfies the version-1 protocol invariants.
145 ///
146 /// The returned [`ValidationViolation`] list is structured so callers can
147 /// log individual fields and decide whether to reject the snapshot,
148 /// surface it as a warning, or fall back to a warming-up health response.
149 ///
150 /// # Invariants
151 ///
152 /// - `schema_version == SCHEMA_VERSION_V1`.
153 /// - `observed_at_unix_ms > 0` and `sample_interval_ms > 0`.
154 /// - `cpu.logical_cores > 0`.
155 /// - All percentages are finite and in `0.0..=100.0`.
156 /// - `used_bytes <= total_bytes` for memory and swap.
157 /// - When `total_bytes == 0`, the corresponding `usage_pct` is `0.0`.
158 /// - `iowait_pct` is `None` exactly when `cpu_iowait` capability is
159 /// `false`. A `true` capability with `None` is rejected because a
160 /// `Ready` snapshot must report every supported metric.
161 pub fn validate(&self) -> Result<(), Vec<ValidationViolation>> {
162 crate::validate::validate(self)
163 }
164}