use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PeerStatus {
Healthy,
Suspected,
Down,
}
#[derive(Debug, Clone)]
pub struct PeerInfo {
pub server_id: String,
pub address: String,
pub status: PeerStatus,
pub last_seen: Instant,
pub missed_heartbeats: u32,
pub bytes_received: u64,
pub bytes_sent: u64,
}
impl PeerInfo {
pub fn new(server_id: impl Into<String>, address: impl Into<String>) -> Self {
Self {
server_id: server_id.into(),
address: address.into(),
status: PeerStatus::Healthy,
last_seen: Instant::now(),
missed_heartbeats: 0,
bytes_received: 0,
bytes_sent: 0,
}
}
pub fn is_healthy(&self) -> bool {
self.status == PeerStatus::Healthy
}
pub fn heartbeat_received(&mut self) {
self.status = PeerStatus::Healthy;
self.missed_heartbeats = 0;
self.last_seen = Instant::now();
}
pub fn heartbeat_missed(&mut self) {
self.missed_heartbeats += 1;
if self.missed_heartbeats >= 2 {
self.status = PeerStatus::Suspected;
}
if self.missed_heartbeats >= 5 {
self.status = PeerStatus::Down;
}
}
pub fn add_bytes_received(&mut self, bytes: u64) {
self.bytes_received = self.bytes_received.saturating_add(bytes);
}
pub fn add_bytes_sent(&mut self, bytes: u64) {
self.bytes_sent = self.bytes_sent.saturating_add(bytes);
}
pub fn time_since_heartbeat(&self) -> Duration {
self.last_seen.elapsed()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_peer_info_creation() {
let peer = PeerInfo::new("server_2", "127.0.0.1:9001");
assert_eq!(peer.server_id, "server_2");
assert_eq!(peer.address, "127.0.0.1:9001");
assert_eq!(peer.status, PeerStatus::Healthy);
assert!(peer.is_healthy());
}
#[test]
fn test_peer_heartbeat_received() {
let mut peer = PeerInfo::new("server_2", "127.0.0.1:9001");
peer.missed_heartbeats = 3;
peer.status = PeerStatus::Suspected;
peer.heartbeat_received();
assert_eq!(peer.status, PeerStatus::Healthy);
assert_eq!(peer.missed_heartbeats, 0);
assert!(peer.is_healthy());
}
#[test]
fn test_peer_heartbeat_missed() {
let mut peer = PeerInfo::new("server_2", "127.0.0.1:9001");
peer.heartbeat_missed();
assert_eq!(peer.missed_heartbeats, 1);
assert_eq!(peer.status, PeerStatus::Healthy);
peer.heartbeat_missed();
assert_eq!(peer.missed_heartbeats, 2);
assert_eq!(peer.status, PeerStatus::Suspected);
for _ in 0..3 {
peer.heartbeat_missed();
}
assert_eq!(peer.status, PeerStatus::Down); }
#[test]
fn test_peer_bytes_tracking() {
let mut peer = PeerInfo::new("server_2", "127.0.0.1:9001");
assert_eq!(peer.bytes_received, 0);
assert_eq!(peer.bytes_sent, 0);
peer.add_bytes_received(1024);
peer.add_bytes_sent(512);
assert_eq!(peer.bytes_received, 1024);
assert_eq!(peer.bytes_sent, 512);
}
#[test]
fn test_peer_time_since_heartbeat() {
let peer = PeerInfo::new("server_2", "127.0.0.1:9001");
let elapsed = peer.time_since_heartbeat();
assert!(elapsed.as_millis() < 100); }
}