1use crate::models::{Column, DataType, Row, Value};
4
5pub fn status_columns() -> Vec<Column> {
6 vec![
7 Column::new("Version", DataType::Text),
8 Column::new("Uptime (s)", DataType::Text),
9 Column::new("Connections", DataType::Text),
10 Column::new("QPS", DataType::Text),
11 Column::new("Cluster Schema", DataType::Text),
12 Column::new("Cluster Mode", DataType::Text),
13 Column::new("Node ID", DataType::Text),
14 Column::new("Lifecycle", DataType::Text),
15 Column::new("Degraded", DataType::Bool),
16 Column::new("Local Only", DataType::Bool),
17 Column::new("Future Distributed", DataType::Bool),
18 Column::new("Scatter/Gather", DataType::Bool),
19 Column::new("Diagnostics", DataType::Text),
20 ]
21}
22
23#[derive(Debug, Clone, Copy, Default)]
24pub struct StatusRowFields<'a> {
25 pub version: Option<&'a str>,
26 pub uptime_secs: Option<u64>,
27 pub connections: Option<u64>,
28 pub qps: Option<f64>,
29 pub cluster_schema_version: Option<u32>,
30 pub cluster_mode: Option<&'a str>,
31 pub node_id: Option<&'a str>,
32 pub lifecycle_state: Option<&'a str>,
33 pub degraded: Option<bool>,
34 pub local_only: Option<bool>,
35 pub future_distributed: Option<bool>,
36 pub scatter_gather: Option<bool>,
37 pub diagnostics: Option<&'a str>,
38}
39
40pub fn status_row(fields: StatusRowFields<'_>) -> Row {
41 Row::new(vec![
42 Value::Text(opt_text(fields.version)),
43 Value::Text(opt_u64(fields.uptime_secs)),
44 Value::Text(opt_u64(fields.connections)),
45 Value::Text(opt_f64(fields.qps)),
46 Value::Text(opt_u32(fields.cluster_schema_version)),
47 Value::Text(opt_text(fields.cluster_mode)),
48 Value::Text(opt_text(fields.node_id)),
49 Value::Text(opt_text(fields.lifecycle_state)),
50 opt_bool_value(fields.degraded),
51 opt_bool_value(fields.local_only),
52 opt_bool_value(fields.future_distributed),
53 opt_bool_value(fields.scatter_gather),
54 Value::Text(opt_text(fields.diagnostics)),
55 ])
56}
57
58pub fn metrics_columns() -> Vec<Column> {
59 vec![
60 Column::new("QPS", DataType::Text),
61 Column::new("Avg Latency (ms)", DataType::Text),
62 Column::new("P99 Latency (ms)", DataType::Text),
63 Column::new("Memory (MB)", DataType::Text),
64 Column::new("Active Connections", DataType::Text),
65 ]
66}
67
68pub fn metrics_row(
69 qps: Option<f64>,
70 avg_latency_ms: Option<f64>,
71 p99_latency_ms: Option<f64>,
72 memory_usage_mb: Option<u64>,
73 active_connections: Option<u64>,
74) -> Row {
75 Row::new(vec![
76 Value::Text(opt_f64(qps)),
77 Value::Text(opt_f64(avg_latency_ms)),
78 Value::Text(opt_f64(p99_latency_ms)),
79 Value::Text(opt_u64(memory_usage_mb)),
80 Value::Text(opt_u64(active_connections)),
81 ])
82}
83
84pub fn health_columns() -> Vec<Column> {
85 vec![
86 Column::new("Status", DataType::Text),
87 Column::new("Message", DataType::Text),
88 Column::new("Degraded", DataType::Bool),
89 Column::new("Cluster Mode", DataType::Text),
90 Column::new("Node ID", DataType::Text),
91 ]
92}
93
94pub fn health_row(
95 status: Option<&str>,
96 message: Option<&str>,
97 degraded: Option<bool>,
98 cluster_mode: Option<&str>,
99 node_id: Option<&str>,
100) -> Row {
101 Row::new(vec![
102 Value::Text(opt_text(status)),
103 Value::Text(opt_text(message)),
104 opt_bool_value(degraded),
105 Value::Text(opt_text(cluster_mode)),
106 Value::Text(opt_text(node_id)),
107 ])
108}
109
110pub fn compaction_columns() -> Vec<Column> {
111 vec![
112 Column::new("Status", DataType::Text),
113 Column::new("Message", DataType::Text),
114 ]
115}
116
117pub fn compaction_row(success: Option<bool>, message: Option<&str>) -> Row {
118 let result = match success {
119 Some(true) => "OK",
120 Some(false) => "Failed",
121 None => "N/A",
122 };
123 Row::new(vec![
124 Value::Text(result.to_string()),
125 Value::Text(opt_text(message)),
126 ])
127}
128
129pub fn cluster_operation_columns() -> Vec<Column> {
130 vec![
131 Column::new("Action", DataType::Text),
132 Column::new("Mode", DataType::Text),
133 Column::new("Node", DataType::Text),
134 Column::new("Lifecycle", DataType::Text),
135 Column::new("Degraded", DataType::Text),
136 ]
137}
138
139pub fn cluster_operation_row(
140 action: Option<&str>,
141 mode: Option<&str>,
142 node_id: Option<&str>,
143 lifecycle_state: Option<&str>,
144 degraded: Option<bool>,
145) -> Row {
146 Row::new(vec![
147 Value::Text(opt_text(action)),
148 Value::Text(opt_text(mode)),
149 Value::Text(opt_text(node_id)),
150 Value::Text(opt_text(lifecycle_state)),
151 Value::Text(opt_bool(degraded)),
152 ])
153}
154
155#[derive(Debug, Clone, Copy)]
159pub struct ClusterManagementRowFields<'a> {
160 pub operation_id: &'a str,
161 pub operation: &'a str,
162 pub outcome_class: &'a str,
163 pub reason: &'a str,
164 pub state_version: Option<u64>,
165 pub control_available: bool,
166 pub control_mode: &'a str,
167 pub control_reason: &'a str,
168 pub missing_prerequisites: Option<&'a str>,
169 pub actor: Option<&'a str>,
170}
171
172pub fn cluster_management_columns() -> Vec<Column> {
173 vec![
174 Column::new("Operation ID", DataType::Text),
175 Column::new("Operation", DataType::Text),
176 Column::new("Outcome", DataType::Text),
177 Column::new("Reason", DataType::Text),
178 Column::new("State Version", DataType::Text),
179 Column::new("Control Available", DataType::Bool),
180 Column::new("Control Mode", DataType::Text),
181 Column::new("Control Reason", DataType::Text),
182 Column::new("Missing Prerequisites", DataType::Text),
183 Column::new("Actor", DataType::Text),
184 ]
185}
186
187pub fn cluster_management_row(fields: ClusterManagementRowFields<'_>) -> Row {
188 Row::new(vec![
189 Value::Text(fields.operation_id.to_string()),
190 Value::Text(fields.operation.to_string()),
191 Value::Text(fields.outcome_class.to_string()),
192 Value::Text(fields.reason.to_string()),
193 Value::Text(opt_u64(fields.state_version)),
194 Value::Bool(fields.control_available),
195 Value::Text(fields.control_mode.to_string()),
196 Value::Text(fields.control_reason.to_string()),
197 Value::Text(opt_text(fields.missing_prerequisites)),
198 Value::Text(opt_text(fields.actor)),
199 ])
200}
201
202fn opt_text(value: Option<&str>) -> String {
203 value.unwrap_or("N/A").to_string()
204}
205
206fn opt_u64(value: Option<u64>) -> String {
207 value
208 .map(|value| value.to_string())
209 .unwrap_or_else(|| "N/A".to_string())
210}
211
212fn opt_u32(value: Option<u32>) -> String {
213 value
214 .map(|value| value.to_string())
215 .unwrap_or_else(|| "N/A".to_string())
216}
217
218fn opt_f64(value: Option<f64>) -> String {
219 value
220 .map(|value| format!("{:.2}", value))
221 .unwrap_or_else(|| "N/A".to_string())
222}
223
224fn opt_bool(value: Option<bool>) -> String {
225 value
226 .map(|value| value.to_string())
227 .unwrap_or_else(|| "N/A".to_string())
228}
229
230fn opt_bool_value(value: Option<bool>) -> Value {
231 value.map(Value::Bool).unwrap_or(Value::Null)
232}
233
234#[cfg(test)]
235mod cluster_management_tests {
236 use super::*;
237
238 #[test]
239 fn cluster_management_row_contains_machine_contract_fields() {
240 let columns = cluster_management_columns();
241 let row = cluster_management_row(ClusterManagementRowFields {
242 operation_id: "operation-7",
243 operation: "ranges_register",
244 outcome_class: "pending",
245 reason: "metadata_consensus_adapter_not_attached",
246 state_version: None,
247 control_available: true,
248 control_mode: "cluster_aware",
249 control_reason: "ready",
250 missing_prerequisites: None,
251 actor: Some("operator-a"),
252 });
253
254 assert_eq!(columns.len(), row.columns.len());
255 assert_eq!(row.columns[0], Value::Text("operation-7".to_string()));
256 assert_eq!(row.columns[2], Value::Text("pending".to_string()));
257 assert_eq!(row.columns[5], Value::Bool(true));
258 }
259}