Files
fips/src/bin/fipstop/ui/routing.rs
Johnathan Corgan 13c0b70dc3 Add rustfmt formatting policy and reformat codebase
Add rustfmt.toml with stable defaults and apply cargo fmt to all
source files. This establishes a consistent formatting baseline
for CI enforcement.
2026-04-10 08:27:07 +00:00

298 lines
9.5 KiB
Rust

use ratatui::Frame;
use ratatui::layout::{Constraint, Layout, Rect};
use ratatui::style::{Color, Style};
use ratatui::text::Line;
use ratatui::widgets::{Block, Borders, Paragraph};
use crate::app::{App, Tab};
use super::helpers;
pub fn draw(frame: &mut Frame, app: &App, area: Rect) {
let data = match app.data.get(&Tab::Routing) {
Some(d) => d,
None => {
let msg =
Paragraph::new(" Waiting for data...").style(Style::default().fg(Color::DarkGray));
frame.render_widget(msg, area);
return;
}
};
let chunks = Layout::vertical([
Constraint::Length(7), // Routing State
Constraint::Length(8), // Coordinate Cache
Constraint::Min(3), // Routing Statistics
])
.split(area);
draw_routing_state(frame, data, chunks[0]);
draw_coord_cache(frame, app, chunks[1]);
draw_routing_stats(frame, data, chunks[2]);
}
fn draw_routing_state(frame: &mut Frame, data: &serde_json::Value, area: Rect) {
let lines = vec![
helpers::kv_line(
"Coord Cache",
&helpers::u64_field(data, "coord_cache_entries"),
),
helpers::kv_line(
"Identity Cache",
&helpers::u64_field(data, "identity_cache_entries"),
),
helpers::kv_line(
"Pending Lookups",
&helpers::u64_field(data, "pending_lookups"),
),
helpers::kv_line(
"Recent Requests",
&helpers::u64_field(data, "recent_requests"),
),
];
let block = Block::default()
.borders(Borders::ALL)
.title(" Routing State ");
let inner = block.inner(area);
frame.render_widget(block, area);
frame.render_widget(Paragraph::new(lines), inner);
}
/// Format a forwarding counter as "N pkts (formatted_bytes)".
fn fwd_line(data: &serde_json::Value, label: &str, pkt_key: &str, byte_key: &str) -> Line<'static> {
let pkts = data
.get("forwarding")
.and_then(|f| f.get(pkt_key))
.and_then(|v| v.as_u64())
.unwrap_or(0);
let bytes = data
.get("forwarding")
.and_then(|f| f.get(byte_key))
.and_then(|v| v.as_u64())
.unwrap_or(0);
helpers::kv_line(
label,
&format!("{} pkts ({})", pkts, helpers::format_bytes(bytes)),
)
}
fn draw_routing_stats(frame: &mut Frame, data: &serde_json::Value, area: Rect) {
let block = Block::default()
.borders(Borders::ALL)
.title(" Routing Statistics ");
let inner = block.inner(area);
frame.render_widget(block, area);
let cols =
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]).split(inner);
// Left column: Forwarding + Discovery
let mut left = vec![
helpers::section_header("Forwarding"),
fwd_line(data, "Received", "received_packets", "received_bytes"),
fwd_line(data, "Delivered", "delivered_packets", "delivered_bytes"),
fwd_line(data, "Forwarded", "forwarded_packets", "forwarded_bytes"),
fwd_line(data, "Originated", "originated_packets", "originated_bytes"),
fwd_line(
data,
"Decode Error",
"decode_error_packets",
"decode_error_bytes",
),
fwd_line(
data,
"TTL Exhausted",
"ttl_exhausted_packets",
"ttl_exhausted_bytes",
),
fwd_line(
data,
"No Route",
"drop_no_route_packets",
"drop_no_route_bytes",
),
fwd_line(
data,
"MTU Exceeded",
"drop_mtu_exceeded_packets",
"drop_mtu_exceeded_bytes",
),
fwd_line(
data,
"Send Error",
"drop_send_error_packets",
"drop_send_error_bytes",
),
Line::from(""),
helpers::section_header("Discovery Requests"),
helpers::kv_line(
"Received",
&helpers::nested_u64(data, "discovery", "req_received"),
),
helpers::kv_line(
"Forwarded",
&helpers::nested_u64(data, "discovery", "req_forwarded"),
),
helpers::kv_line(
"Initiated",
&helpers::nested_u64(data, "discovery", "req_initiated"),
),
helpers::kv_line(
"Deduplicated",
&helpers::nested_u64(data, "discovery", "req_deduplicated"),
),
helpers::kv_line(
"Target Is Us",
&helpers::nested_u64(data, "discovery", "req_target_is_us"),
),
helpers::kv_line(
"Duplicate",
&helpers::nested_u64(data, "discovery", "req_duplicate"),
),
helpers::kv_line(
"Bloom Miss",
&helpers::nested_u64(data, "discovery", "req_bloom_miss"),
),
helpers::kv_line(
"Backoff Suppressed",
&helpers::nested_u64(data, "discovery", "req_backoff_suppressed"),
),
helpers::kv_line(
"Fwd Rate Limited",
&helpers::nested_u64(data, "discovery", "req_forward_rate_limited"),
),
helpers::kv_line(
"TTL Exhausted",
&helpers::nested_u64(data, "discovery", "req_ttl_exhausted"),
),
helpers::kv_line(
"Decode Error",
&helpers::nested_u64(data, "discovery", "req_decode_error"),
),
Line::from(""),
helpers::section_header("Discovery Responses"),
helpers::kv_line(
"Received",
&helpers::nested_u64(data, "discovery", "resp_received"),
),
helpers::kv_line(
"Accepted",
&helpers::nested_u64(data, "discovery", "resp_accepted"),
),
helpers::kv_line(
"Forwarded",
&helpers::nested_u64(data, "discovery", "resp_forwarded"),
),
helpers::kv_line(
"Timed Out",
&helpers::nested_u64(data, "discovery", "resp_timed_out"),
),
helpers::kv_line(
"Identity Miss",
&helpers::nested_u64(data, "discovery", "resp_identity_miss"),
),
helpers::kv_line(
"Proof Failed",
&helpers::nested_u64(data, "discovery", "resp_proof_failed"),
),
helpers::kv_line(
"Decode Error",
&helpers::nested_u64(data, "discovery", "resp_decode_error"),
),
];
// Right column: Error Signals + Congestion
let mut right = vec![
helpers::section_header("Error Signals"),
helpers::kv_line(
"Coords Required",
&helpers::nested_u64(data, "error_signals", "coords_required"),
),
helpers::kv_line(
"Path Broken",
&helpers::nested_u64(data, "error_signals", "path_broken"),
),
helpers::kv_line(
"MTU Exceeded",
&helpers::nested_u64(data, "error_signals", "mtu_exceeded"),
),
Line::from(""),
helpers::section_header("Congestion"),
helpers::kv_line(
"CE Forwarded",
&helpers::nested_u64(data, "congestion", "ce_forwarded"),
),
helpers::kv_line(
"CE Received",
&helpers::nested_u64(data, "congestion", "ce_received"),
),
helpers::kv_line(
"Congestion Detected",
&helpers::nested_u64(data, "congestion", "congestion_detected"),
),
helpers::kv_line(
"Kernel Drops",
&helpers::nested_u64(data, "congestion", "kernel_drop_events"),
),
];
let max_lines = cols[0].height as usize;
left.truncate(max_lines);
right.truncate(max_lines);
frame.render_widget(Paragraph::new(left), cols[0]);
frame.render_widget(Paragraph::new(right), cols[1]);
}
fn draw_coord_cache(frame: &mut Frame, app: &App, area: Rect) {
let data = match app.data.get(&Tab::Cache) {
Some(d) => d,
None => {
let block = Block::default()
.borders(Borders::ALL)
.title(" Coordinate Cache ");
let inner = block.inner(area);
frame.render_widget(block, area);
let msg =
Paragraph::new(" Waiting for data...").style(Style::default().fg(Color::DarkGray));
frame.render_widget(msg, inner);
return;
}
};
let entries = helpers::u64_field(data, "entries");
let max_entries = helpers::u64_field(data, "max_entries");
let fill_pct = data
.get("fill_ratio")
.and_then(|v| v.as_f64())
.map(|r| format!("{:.1}%", r * 100.0))
.unwrap_or_else(|| "-".into());
let ttl = data
.get("default_ttl_ms")
.and_then(|v| v.as_u64())
.map(helpers::format_duration_ms)
.unwrap_or_else(|| "-".into());
let expired = helpers::u64_field(data, "expired");
let avg_age = data
.get("avg_age_ms")
.and_then(|v| v.as_u64())
.map(helpers::format_duration_ms)
.unwrap_or_else(|| "-".into());
let lines = vec![
helpers::kv_line("Entries", &format!("{entries} / {max_entries}")),
helpers::kv_line("Fill Ratio", &fill_pct),
helpers::kv_line("Default TTL", &ttl),
helpers::kv_line("Expired", &expired),
helpers::kv_line("Avg Age", &avg_age),
];
let block = Block::default()
.borders(Borders::ALL)
.title(" Coordinate Cache ");
let inner = block.inner(area);
frame.render_widget(block, area);
frame.render_widget(Paragraph::new(lines), inner);
}