use std::fmt;
use std::io::ErrorKind;
use std::net::{IpAddr, SocketAddr};
use std::sync::Arc;
use std::time::Duration;
use super::Channel;
#[derive(Clone)]
pub struct Say(Arc<dyn Fn(&str) + Send + Sync>);
impl Say {
pub fn new(sink: Arc<dyn Fn(&str) + Send + Sync>) -> Say {
Say(sink)
}
fn line(&self, line: &str) {
(self.0)(line);
}
}
impl Default for Say {
fn default() -> Say {
Say(Arc::new(|line: &str| eprintln!("{line}")))
}
}
impl fmt::Debug for Say {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Say")
}
}
pub(crate) fn tell(ch: &Channel, source: &'static str, lines: Vec<String>) {
let news = crate::state::worked(&ch.key, |w| {
w.said.insert(source, lines.clone()).as_ref() != Some(&lines)
});
if news {
for line in &lines {
ch.roving.say.line(line);
}
}
}
const P: &str = "lernie: rendezvous:";
pub(crate) fn taken() -> String {
format!("{P} held line taken up — no dial")
}
pub(crate) fn dropped(why: &str) -> String {
format!("{P} held line dropped — {why}")
}
pub(crate) fn direct(outcome: Result<(), ErrorKind>, within: Duration) -> String {
match outcome {
Ok(()) => format!("{P} direct rung answered"),
Err(ErrorKind::TimedOut) => format!("{P} direct rung timed out after {within:?}"),
Err(kind) => format!("{P} direct rung refused — {kind}"),
}
}
pub(crate) fn no_bootstrap() -> String {
format!("{P} no bootstrap node resolved — no rendezvous")
}
pub(crate) fn withheld(what: &str) -> String {
format!("{P} {what} — the DHT walk failed (reason withheld: it names nodes)")
}
pub(crate) fn no_presence() -> String {
format!("{P} no presence is published under this pairing")
}
pub(crate) fn unopened(seq: i64) -> String {
format!("{P} presence seq {seq} did not open under this pairing's seal key")
}
pub(crate) fn presence(seq: i64, endpoints: &[SocketAddr]) -> String {
format!("{P} presence read — seq {seq}, {}", counted(endpoints))
}
pub(crate) fn overlay(ips: &[IpAddr]) -> Option<String> {
(!ips.is_empty() && ips.iter().all(overlaid)).then(|| {
format!(
"{P} every address the call carries ({}) is in an overlay or carrier-NAT range — an engine outside that network cannot reach it",
families(ips)
)
})
}
pub(crate) fn call(nonce: u64, seq: i64, endpoints: &[SocketAddr], acks: usize) -> String {
format!(
"{P} call nonce {nonce} written — seq {seq}, {}, {acks} ack(s)",
counted(endpoints)
)
}
pub(crate) fn recall(nonce: u64, seq: i64, endpoints: &[SocketAddr], acks: usize) -> String {
format!(
"{P} re-call from cached presence — nonce {nonce}, seq {seq}, {}, {acks} ack(s)",
counted(endpoints)
)
}
pub(crate) fn started(what: &str, targets: &[SocketAddr], window: Duration) -> String {
format!(
"{P} {what} started — {}, window {window:?}",
counted(targets)
)
}
pub(crate) fn landed(what: &str, peer: Option<IpAddr>) -> String {
format!("{P} {what} landed ({})", families(&Vec::from_iter(peer)))
}
pub(crate) fn expired(what: &str, window: Duration) -> String {
format!("{P} {what} expired after {window:?} with no stream")
}
pub(crate) fn kept() -> String {
format!("{P} punched line held between asks")
}
pub(crate) fn pinged() -> String {
format!("{P} held line ping discarded")
}
fn counted(endpoints: &[SocketAddr]) -> String {
let ips: Vec<IpAddr> = endpoints.iter().map(SocketAddr::ip).collect();
format!("{} endpoint(s) ({})", ips.len(), families(&ips))
}
fn families(ips: &[IpAddr]) -> String {
let v6 = ips.iter().filter(|ip| ip.is_ipv6()).count();
let parts: Vec<String> = [(v6, "v6"), (ips.len() - v6, "v4")]
.into_iter()
.filter(|(n, _)| *n > 0)
.map(|(n, family)| format!("{n} {family}"))
.collect();
if parts.is_empty() {
"none".to_owned()
} else {
parts.join(", ")
}
}
fn overlaid(ip: &IpAddr) -> bool {
match ip {
IpAddr::V4(v4) => {
let [a, b, ..] = v4.octets();
a == 100 && b & 0xc0 == 0x40
}
IpAddr::V6(v6) => v6.segments()[0] & 0xfe00 == 0xfc00,
}
}
#[cfg(test)]
mod tests;