use std::net::{IpAddr, SocketAddr};
use std::sync::Arc;
use std::time::Duration;
pub(crate) type Say = Arc<dyn Fn(&str) + Send + Sync>;
pub(crate) fn stderr() -> Say {
Arc::new(|line: &str| eprintln!("{line}"))
}
const P: &str = "yog: rendezvous:";
pub(crate) fn started(punch_port: u16, dht_port: u16) -> String {
format!("{P} started — punch port {punch_port}, DHT socket port {dht_port}")
}
pub(crate) fn published(seq: i64, endpoints: usize, acks: usize) -> String {
format!("{P} presence published — seq {seq}, {endpoints} endpoint(s), {acks} ack(s)")
}
pub(crate) fn not_published() -> String {
format!("{P} presence not published — the DHT walk failed (reason withheld: it names nodes)")
}
pub(crate) fn poll_failed() -> String {
format!("{P} inbox poll failed — the DHT walk failed (reason withheld: it names nodes)")
}
pub(crate) fn unverified(seq: i64) -> String {
format!("{P} inbox item seq {seq} did not verify under the pairing's seal key — no punch")
}
pub(crate) fn unopened(seq: i64) -> String {
format!("{P} inbox item seq {seq} verified but is not a call — no punch")
}
pub(crate) fn seen(nonce: u64) -> String {
format!("{P} call nonce {nonce} already punched — no punch")
}
pub(crate) fn opened(nonce: u64, endpoints: &[SocketAddr]) -> String {
let ips: Vec<IpAddr> = endpoints.iter().map(SocketAddr::ip).collect();
format!(
"{P} call nonce {nonce} opened — {} endpoint(s) ({}) — punch started",
endpoints.len(),
families(&ips)
)
}
pub(crate) fn landed(nonce: u64, peers: &[IpAddr]) -> String {
format!(
"{P} punch for nonce {nonce} landed {} stream(s) ({})",
peers.len(),
families(peers)
)
}
pub(crate) fn expired(nonce: u64, window: Duration) -> String {
format!(
"{P} punch for nonce {nonce} expired after {}s with no stream",
window.as_secs()
)
}
pub(crate) fn ended(nonce: u64) -> String {
format!("{P} served stream for nonce {nonce} ended")
}
pub(crate) fn accepted(peer: Option<IpAddr>) -> String {
format!(
"{P} stream accepted on the punch port ({}) — served",
family(peer)
)
}
pub(crate) fn refused(peer: Option<IpAddr>) -> String {
format!(
"{P} stream on the punch port ({}) refused at the handshake — dropped",
family(peer)
)
}
fn family(peer: Option<IpAddr>) -> &'static str {
match peer {
Some(IpAddr::V6(_)) => "v6",
Some(IpAddr::V4(_)) => "v4",
None => "family unknown",
}
}
fn families(ips: &[IpAddr]) -> String {
let v6 = ips.iter().filter(|ip| ip.is_ipv6()).count();
let v4 = ips.len() - v6;
let parts: Vec<String> = [(v6, "v6"), (v4, "v4")]
.into_iter()
.filter(|(n, _)| *n > 0)
.map(|(n, family)| format!("{n} {family}"))
.collect();
if parts.is_empty() {
"none".to_owned()
} else {
parts.join(", ")
}
}
#[cfg(test)]
mod tests;