From 4d2f8a54ec8e9b98d9cbf9a3e176b280c4caf9b7 Mon Sep 17 00:00:00 2001 From: Ultradesu Date: Fri, 2 Oct 2026 11:26:13 +0100 Subject: [PATCH] Compact the status address list to one line per address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `tsunagi status` printed every local address as two lines: the address, then an indented note holding the interface (and, for candidates, a priority rank). On a host with many interfaces — common on macOS, where each VPN adds a utun and every interface carries a link-local — that made the "local addresses" section very long and hard to scan. Each address is now a single line: the announce status stays the label (announced / not announced / loopback) and the address and its interface share the detail, as ` · `. The per-row note and the candidate-priority rank are dropped; ordering still reflects candidate priority. No other section changes, and nothing but the presentation does. Checks: cargo fmt --all --check, cargo clippy --locked --workspace --all-targets -D warnings, and the tsunagi-cli tests all pass on macOS; verified by eye against a live `tsunagi status`. Co-Authored-By: Claude Opus 4.8 --- crates/tsunagi-cli/src/main.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/crates/tsunagi-cli/src/main.rs b/crates/tsunagi-cli/src/main.rs index 88f78cb..9f0dbd0 100644 --- a/crates/tsunagi-cli/src/main.rs +++ b/crates/tsunagi-cli/src/main.rs @@ -3061,18 +3061,19 @@ async fn addresses_section() -> report::Section { iface_map.insert(item.addr, item.iface.clone()); } + // One line per address: the announce status is the label, and the address + // and its interface share the detail, so a host with many addresses stays + // compact instead of spending two lines on each. + let detail = |addr: std::net::IpAddr| match iface_map.get(&addr) { + Some(iface) => format!("{addr} · {iface}"), + None => addr.to_string(), + }; + // 1. Announced peer candidate addresses (in candidate priority order) let mut seen = std::collections::HashSet::new(); - for (idx, &cand) in candidates.iter().enumerate() { + for &cand in &candidates { if seen.insert(cand) { - let note = iface_map - .get(&cand) - .map(|iface| format!("{iface} · candidate #{}", idx + 1)); - let row = Row::new(Health::Info, "announced", cand.to_string()); - addresses.push(match note { - Some(n) => row.with_note(n), - None => row, - }); + addresses.push(Row::new(Health::Info, "announced", detail(cand))); } } @@ -3093,16 +3094,13 @@ async fn addresses_section() -> report::Section { unannounced.sort_by_key(|item| item.addr); for item in unannounced { - let note = format!("{} · not announced to peers", item.iface); - addresses - .push(Row::new(Health::Info, "not announced", item.addr.to_string()).with_note(note)); + addresses.push(Row::new(Health::Info, "not announced", detail(item.addr))); } // 3. Loopback addresses loopbacks.sort_by_key(|item| item.addr); for item in loopbacks { - addresses - .push(Row::new(Health::Info, "loopback", item.addr.to_string()).with_note(&item.iface)); + addresses.push(Row::new(Health::Info, "loopback", detail(item.addr))); } addresses