From 9ff67562cbbc8abba3fc3b97831c575db289b8e7 Mon Sep 17 00:00:00 2001 From: Defelo Date: Tue, 24 Feb 2026 17:25:49 +0100 Subject: [PATCH] cli: Format IPv6 addresses in square brackets --- .../radicle-cli/src/commands/node/control.rs | 6 ++-- crates/radicle-cli/src/commands/sync.rs | 4 +-- crates/radicle-cli/src/terminal/format.rs | 24 +------------ crates/radicle/src/node.rs | 36 ++++++++++++++++++- 4 files changed, 41 insertions(+), 29 deletions(-) diff --git a/crates/radicle-cli/src/commands/node/control.rs b/crates/radicle-cli/src/commands/node/control.rs index 63e833f7..cb1473e8 100644 --- a/crates/radicle-cli/src/commands/node/control.rs +++ b/crates/radicle-cli/src/commands/node/control.rs @@ -384,17 +384,17 @@ pub fn sessions(node: &Node) -> Result>, node term::Label::blank(), ), node::State::Attempted => ( - term::format::addr_compact(&sess.addr).into(), + sess.addr.display_compact().to_string().into(), term::Label::from(state_attempted()), term::Label::blank(), ), node::State::Connected { since, .. } => ( - term::format::addr_compact(&sess.addr).into(), + sess.addr.display_compact().to_string().into(), term::Label::from(state_connected()), term::format::dim(now - since).into(), ), node::State::Disconnected { since, .. } => ( - term::format::addr_compact(&sess.addr).into(), + sess.addr.display_compact().to_string().into(), term::Label::from(state_disconnected()), term::format::dim(now - since).into(), ), diff --git a/crates/radicle-cli/src/commands/sync.rs b/crates/radicle-cli/src/commands/sync.rs index 0653214b..1236e72e 100644 --- a/crates/radicle-cli/src/commands/sync.rs +++ b/crates/radicle-cli/src/commands/sync.rs @@ -460,7 +460,7 @@ impl FetcherSpinner { term::format::secondary(progress.succeeded()), term::format::secondary(self.replicas.lower_bound()), term::format::tertiary(term::format::node_id_human_compact(node)), - term::format::tertiary(term::format::addr_compact(addr)), + term::format::tertiary(addr.display_compact()), )) } @@ -477,7 +477,7 @@ impl FetcherSpinner { term::format::secondary(progress.succeeded()), term::format::secondary(self.replicas.lower_bound()), term::format::tertiary(term::format::node_id_human_compact(node)), - term::format::tertiary(term::format::addr_compact(addr)), + term::format::tertiary(addr.display_compact()), )) } diff --git a/crates/radicle-cli/src/terminal/format.rs b/crates/radicle-cli/src/terminal/format.rs index 2e026cd7..f5b702f3 100644 --- a/crates/radicle-cli/src/terminal/format.rs +++ b/crates/radicle-cli/src/terminal/format.rs @@ -8,7 +8,7 @@ pub use radicle_term::{style, Paint}; use radicle::cob::ObjectId; use radicle::identity::Visibility; use radicle::node::policy::Policy; -use radicle::node::{Address, Alias, AliasStore, HostName, NodeId}; +use radicle::node::{Alias, AliasStore, NodeId}; use radicle::prelude::Did; use radicle::profile::{env, Profile}; use radicle::storage::RefUpdate; @@ -32,28 +32,6 @@ pub fn node_id_human(node: &NodeId) -> Paint { Paint::new(node.to_human()) } -#[must_use] -pub fn addr_compact(address: &Address) -> Paint { - let host = match address.host() { - HostName::Ip(ip) => ip.to_string(), - HostName::Dns(dns) => dns.clone(), - HostName::Tor(onion) => { - let onion = onion.to_string(); - let start = onion.chars().take(8).collect::(); - let end = onion - .chars() - .skip(onion.len() - 8 - ".onion".len()) - .collect::(); - format!("{start}…{end}") - } - _ => unreachable!(), - }; - - let port = address.port().to_string(); - - Paint::new(format!("{host}:{port}")) -} - /// Format a git Oid. pub fn oid(oid: impl Into) -> Paint { Paint::new(format!("{:.7}", oid.into())) diff --git a/crates/radicle/src/node.rs b/crates/radicle/src/node.rs index dcea1777..4493b0fa 100644 --- a/crates/radicle/src/node.rs +++ b/crates/radicle/src/node.rs @@ -17,6 +17,7 @@ pub mod sync; pub mod timestamp; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}; +use std::fmt::Display; use std::io::{BufRead, BufReader}; use std::marker::PhantomData; use std::net::IpAddr; @@ -424,7 +425,7 @@ impl TryFrom<&sqlite::Value> for Alias { /// Peer public protocol address. #[derive(Clone, Eq, PartialEq, Debug, Hash, From, Wrapper, WrapperMut, Serialize, Deserialize)] -#[wrapper(Deref, Display)] +#[wrapper(Deref)] #[wrapper_mut(DerefMut)] #[cfg_attr( feature = "schemars", @@ -490,6 +491,39 @@ impl Address { pub fn port(&self) -> u16 { self.0.port } + + pub fn display_compact(&self) -> impl Display { + let host = match self.host() { + HostName::Ip(IpAddr::V4(ip)) => ip.to_string(), + HostName::Ip(IpAddr::V6(ip)) => format!("[{ip}]"), + HostName::Dns(dns) => dns.clone(), + HostName::Tor(onion) => { + let onion = onion.to_string(); + let start = onion.chars().take(8).collect::(); + let end = onion + .chars() + .skip(onion.len() - 8 - ".onion".len()) + .collect::(); + format!("{start}…{end}") + } + _ => unreachable!(), + }; + + let port = self.port().to_string(); + + format!("{host}:{port}") + } +} + +impl Display for Address { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.host() { + HostName::Ip(IpAddr::V6(ip)) => { + write!(f, "[{ip}]:{}", self.port()) + } + _ => self.0.fmt(f), + } + } } impl FromStr for Address {