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. Empty
64/// values are permitted when the source cannot provide an optional identity
65/// field.
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(rename_all = "snake_case")]
68pub struct SystemIdentity {
69 /// User-facing system name (typically a configured alias). Empty values
70 /// are permitted when the source cannot provide this optional identity.
71 pub name: String,
72 /// Network hostname as reported by the operating system. Empty values are
73 /// permitted when the source cannot provide this optional identity.
74 pub hostname: String,
75 /// Operating-system family name (e.g. `"linux"`, `"macos"`).
76 pub os_name: String,
77 /// Operating-system version string.
78 pub os_version: String,
79 /// Kernel name (e.g. `"Linux"`, `"Darwin"`).
80 pub kernel_name: String,
81 /// Kernel release string.
82 pub kernel_release: String,
83 /// Target architecture (e.g. `"x86_64"`, `"aarch64"`).
84 pub architecture: String,
85}
86
87/// CPU utilization snapshot.
88///
89/// `usage_pct` is total CPU busy over the most recent sampling interval.
90/// `iowait_pct` is the aggregate CPU I/O-wait time over the same interval;
91/// it is `Some(_)` only when [`MetricCapabilities::cpu_iowait`] is `true`.
92#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
93#[serde(rename_all = "snake_case")]
94pub struct CpuMetrics {
95 /// Number of logical CPU cores available to the kernel.
96 pub logical_cores: u32,
97 /// Total CPU busy percentage, `0.0..=100.0`, derived from delta samples.
98 pub usage_pct: f32,
99 /// Aggregate CPU I/O-wait percentage, `0.0..=100.0`. `None` when the
100 /// platform does not expose this state.
101 pub iowait_pct: Option<f32>,
102}
103
104/// One-, five-, and fifteen-minute load averages as reported by the
105/// operating system.
106#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
107#[serde(rename_all = "snake_case")]
108pub struct LoadAverage {
109 /// One-minute load average.
110 pub one: f32,
111 /// Five-minute load average.
112 pub five: f32,
113 /// Fifteen-minute load average.
114 pub fifteen: f32,
115}
116
117/// Physical memory utilization.
118///
119/// `usage_pct` is computed as `100.0 * used_bytes / total_bytes` and clamped
120/// to the closed interval `0.0..=100.0`.
121#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
122#[serde(rename_all = "snake_case")]
123pub struct MemoryMetrics {
124 /// Used physical memory in bytes. Never exceeds `total_bytes`.
125 pub used_bytes: u64,
126 /// Total physical memory in bytes.
127 pub total_bytes: u64,
128 /// Memory utilization percentage, `0.0..=100.0`.
129 pub usage_pct: f32,
130}
131
132/// Swap utilization.
133///
134/// When `total_bytes` is zero, `usage_pct` is `0.0` rather than `NaN`.
135#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
136#[serde(rename_all = "snake_case")]
137pub struct SwapMetrics {
138 /// Used swap in bytes. Never exceeds `total_bytes`.
139 pub used_bytes: u64,
140 /// Total swap in bytes.
141 pub total_bytes: u64,
142 /// Swap utilization percentage, `0.0..=100.0`. Zero when
143 /// `total_bytes == 0`.
144 pub usage_pct: f32,
145}
146
147impl StatusSnapshot {
148 /// Validate that every field satisfies the version-1 protocol invariants.
149 ///
150 /// The returned [`ValidationViolation`] list is structured so callers can
151 /// log individual fields and decide whether to reject the snapshot,
152 /// surface it as a warning, or fall back to a warming-up health response.
153 ///
154 /// # Invariants
155 ///
156 /// - `schema_version == SCHEMA_VERSION_V1`.
157 /// - `observed_at_unix_ms > 0` and `sample_interval_ms > 0`.
158 /// - `cpu.logical_cores > 0`.
159 /// - All percentages are finite and in `0.0..=100.0`.
160 /// - `used_bytes <= total_bytes` for memory and swap.
161 /// - When `total_bytes == 0`, the corresponding `usage_pct` is `0.0`.
162 /// - `iowait_pct` is `None` exactly when `cpu_iowait` capability is
163 /// `false`. A `true` capability with `None` is rejected because a
164 /// `Ready` snapshot must report every supported metric.
165 pub fn validate(&self) -> Result<(), Vec<ValidationViolation>> {
166 crate::validate::validate(self)
167 }
168}