use std::net::{Ipv4Addr, Ipv6Addr}; use aquatic_common::{ AmortizedIndexMap, IndexMap, SecondsSinceServerStart, ServerStartInstant, ValidUntil, }; use aquatic_http_protocol::common::{AnnounceEvent, InfoHash, PeerId}; use aquatic_http_protocol::response::ResponsePeer; pub trait Ip: ::std::fmt::Debug + Copy + Eq + ::std::hash::Hash {} impl Ip for Ipv4Addr {} impl Ip for Ipv6Addr {} #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum PeerStatus { Seeding, Leeching, Stopped, } impl PeerStatus { /// Determine peer status from announce event and number of bytes left. /// /// Likely, the last branch will be taken most of the time. #[inline] pub fn from_event_and_bytes_left(event: AnnounceEvent, opt_bytes_left: Option) -> Self { if let AnnounceEvent::Stopped = event { Self::Stopped } else if let Some(0) = opt_bytes_left { Self::Seeding } else { Self::Leeching } } } #[derive(Debug, Clone, Copy)] pub struct Peer { pub ip_address: I, pub port: u16, pub status: PeerStatus, pub valid_until: ValidUntil, } impl Peer { pub fn to_response_peer(&self) -> ResponsePeer { ResponsePeer { ip_address: self.ip_address, port: self.port, } } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PeerMapKey { pub peer_id: PeerId, pub ip_address: I, } pub type PeerMap = IndexMap, Peer>; pub struct TorrentData { pub peers: PeerMap, pub num_seeders: usize, pub num_leechers: usize, } impl Default for TorrentData { #[inline] fn default() -> Self { Self { peers: Default::default(), num_seeders: 0, num_leechers: 0, } } } pub type TorrentMap = AmortizedIndexMap>; #[derive(Default)] pub struct TorrentMaps { pub ipv4: TorrentMap, pub ipv6: TorrentMap, } impl TorrentMaps { pub fn clean(&mut self, server_start_instant: ServerStartInstant) { let now = server_start_instant.seconds_elapsed(); Self::clean_torrent_map(&mut self.ipv4, now); Self::clean_torrent_map(&mut self.ipv6, now); } fn clean_torrent_map(torrent_map: &mut TorrentMap, now: SecondsSinceServerStart) { torrent_map.retain(|_, torrent_data| { let num_seeders = &mut torrent_data.num_seeders; let num_leechers = &mut torrent_data.num_leechers; torrent_data.peers.retain(|_, peer| { if peer.valid_until.valid(now) { true } else { match peer.status { PeerStatus::Seeding => { *num_seeders -= 1; } PeerStatus::Leeching => { *num_leechers -= 1; } _ => (), }; false } }); !torrent_data.peers.is_empty() }); torrent_map.shrink_to_fit(); } }