use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConnectionState {
Disconnected,
Connecting,
Handshaking,
Connected,
Closing,
}
#[derive(Debug, Clone)]
pub struct PeerMetrics {
pub latency: Option<Duration>,
pub uptime: Duration,
pub ping_count: u32,
pub ping_failures: u32,
pub last_ping: Option<Instant>,
}
impl Default for PeerMetrics {
fn default() -> Self {
Self {
latency: None,
uptime: Duration::ZERO,
ping_count: 0,
ping_failures: 0,
last_ping: None,
}
}
}
impl PeerMetrics {
pub fn update_latency(&mut self, new_latency: Duration) {
const ALPHA: f64 = 0.3; self.latency = Some(
self.latency
.map(|old| {
let old_ms = old.as_secs_f64() * 1000.0;
let new_ms = new_latency.as_secs_f64() * 1000.0;
let smoothed = ALPHA * new_ms + (1.0 - ALPHA) * old_ms;
Duration::from_millis(smoothed as u64)
})
.unwrap_or(new_latency),
);
self.ping_count += 1;
self.last_ping = Some(Instant::now());
}
pub fn record_ping_failure(&mut self) {
self.ping_failures += 1;
}
pub fn reliability_score(&self) -> f32 {
let total_pings = self.ping_count + self.ping_failures;
if total_pings == 0 {
return 0.5; }
(self.ping_count as f32 / total_pings as f32).clamp(0.0, 1.0)
}
}
#[derive(Debug, Clone)]
pub struct PeerInfo {
pub node_id: String,
pub address: SocketAddr,
pub state: ConnectionState,
pub protocol_version: Option<u8>,
pub last_seen: Option<Instant>,
pub connected_at: Option<Instant>,
pub connection_attempts: u32,
pub added_at: Instant, pub metrics: PeerMetrics, }
impl PeerInfo {
pub fn new(node_id: String, address: SocketAddr) -> Self {
Self {
node_id,
address,
state: ConnectionState::Disconnected,
protocol_version: None,
last_seen: None,
connected_at: None,
connection_attempts: 0,
added_at: Instant::now(),
metrics: PeerMetrics::default(),
}
}
pub fn is_connected(&self) -> bool {
self.state == ConnectionState::Connected
}
pub fn update_last_seen(&mut self) {
self.last_seen = Some(Instant::now());
}
pub fn update_uptime(&mut self) {
if let Some(connected_at) = self.connected_at {
self.metrics.uptime = connected_at.elapsed();
} else {
self.metrics.uptime = Duration::ZERO;
}
}
pub fn get_uptime(&self) -> Duration {
if let Some(connected_at) = self.connected_at {
connected_at.elapsed()
} else {
Duration::ZERO
}
}
pub fn is_stale(&self, timeout: Duration) -> bool {
if self.added_at.elapsed() < Duration::from_secs(30) {
return false;
}
if let Some(last_seen) = self.last_seen {
last_seen.elapsed() > timeout
} else {
self.added_at.elapsed() > timeout
}
}
}