1use core::fmt;
2use serde::{Serialize, Serializer};
3use std::collections::{HashMap, HashSet};
4use std::net::SocketAddr;
5use tokio::time::Instant;
6
7#[derive(Default, Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize)]
9pub enum PeerOrigin {
10 Tracker,
11 Listener,
12 Pex,
13 Dht,
14 #[default]
15 Other,
16}
17
18#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize)]
19pub enum TransportProto {
20 Tcp,
21 Utp,
22}
23
24#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
26#[serde(rename_all = "camelCase")]
27pub struct DownloadState {
28 pub am_interested: bool,
29 pub peer_choking: bool,
30 pub bytes_received: usize,
31 pub last_bitrate_bps: usize,
32}
33
34impl Default for DownloadState {
35 fn default() -> Self {
36 Self {
37 am_interested: false,
38 peer_choking: true,
39 bytes_received: 0,
40 last_bitrate_bps: 0,
41 }
42 }
43}
44
45impl fmt::Display for DownloadState {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 write!(
48 f,
49 "am_interested={:<5} peer_choking={:<5} rx_bps={:<8} bytes_recv={:<12}",
50 self.am_interested, self.peer_choking, self.last_bitrate_bps, self.bytes_received
51 )?;
52 Ok(())
53 }
54}
55
56#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
58#[serde(rename_all = "camelCase")]
59pub struct UploadState {
60 pub am_choking: bool,
61 pub peer_interested: bool,
62 pub bytes_sent: usize,
63 pub last_bitrate_bps: usize,
64}
65
66impl Default for UploadState {
67 fn default() -> Self {
68 Self {
69 am_choking: true,
70 peer_interested: false,
71 bytes_sent: 0,
72 last_bitrate_bps: 0,
73 }
74 }
75}
76
77impl fmt::Display for UploadState {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 write!(
80 f,
81 "peer_interested={:<5} am_choking={:<5} tx_bps={:<8} bytes_sent={:<12}",
82 self.peer_interested, self.am_choking, self.last_bitrate_bps, self.bytes_sent
83 )?;
84 Ok(())
85 }
86}
87
88#[derive(Clone, PartialEq, Eq, Debug)]
90pub struct PeerState {
91 pub download: DownloadState,
92 pub upload: UploadState,
93 pub extensions: Option<Box<super::ExtendedHandshake>>,
94 pub origin: PeerOrigin,
95 pub transport: Option<TransportProto>,
96 pub encryption: bool,
97 pub last_download_time: Instant,
98 pub last_upload_time: Instant,
99}
100
101impl Default for PeerState {
102 fn default() -> Self {
103 Self {
104 download: Default::default(),
105 upload: Default::default(),
106 extensions: None,
107 encryption: false,
108 origin: Default::default(),
109 transport: None,
110 last_download_time: Instant::now(),
111 last_upload_time: Instant::now(),
112 }
113 }
114}
115
116impl Serialize for PeerState {
117 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
139 where
140 S: Serializer,
141 {
142 #[derive(Serialize)]
143 struct Data<'a> {
144 download: &'a DownloadState,
145 upload: &'a UploadState,
146 client: &'a str,
147 reqq: Option<usize>,
148 origin: PeerOrigin,
149 proto: Option<TransportProto>,
150 encrypted: bool,
151 }
152 let data = Data {
153 download: &self.download,
154 upload: &self.upload,
155 client: self
156 .extensions
157 .as_ref()
158 .and_then(|ext| ext.client_type.as_deref())
159 .unwrap_or("n/a"),
160 reqq: self.extensions.as_ref().and_then(|ext| ext.request_limit),
161 origin: self.origin,
162 proto: self.transport,
163 encrypted: self.encryption,
164 };
165 data.serialize(serializer)
166 }
167}
168
169#[derive(Default, Debug)]
171pub struct PeerStates {
172 peers: HashMap<SocketAddr, PeerState>,
173 seeders: HashSet<SocketAddr>,
174 leeches: HashSet<SocketAddr>,
175 previously_uploaded_bytes: usize,
176}
177
178impl PeerStates {
179 pub fn update_download(&mut self, remote_ip: &SocketAddr, new_state: &DownloadState) {
181 let state = self.peers.entry(*remote_ip).or_default();
182 if new_state.bytes_received > state.download.bytes_received {
183 state.last_download_time = Instant::now();
184 }
185 state.download = new_state.clone();
186 if state.download.am_interested && !state.download.peer_choking {
187 self.seeders.insert(*remote_ip);
188 } else {
189 self.seeders.remove(remote_ip);
190 }
191 }
192
193 pub fn update_upload(&mut self, remote_ip: &SocketAddr, new_state: &UploadState) {
195 let state = self.peers.entry(*remote_ip).or_default();
196 if new_state.bytes_sent > state.upload.bytes_sent {
197 state.last_upload_time = Instant::now();
198 }
199 state.upload = new_state.clone();
200 if state.upload.peer_interested && !state.upload.am_choking {
201 self.leeches.insert(*remote_ip);
202 } else {
203 self.leeches.remove(remote_ip);
204 }
205 }
206
207 pub fn set_extended_handshake(
209 &mut self,
210 remote_ip: &SocketAddr,
211 extended_handshake: Box<super::ExtendedHandshake>,
212 ) {
213 let state = self.peers.entry(*remote_ip).or_default();
214 if state.extensions.is_none() {
215 state.extensions = Some(extended_handshake);
216 }
217 }
218
219 pub fn set_info(
221 &mut self,
222 remote_ip: &SocketAddr,
223 origin: PeerOrigin,
224 transport: TransportProto,
225 encryption: bool,
226 ) {
227 let state = self.peers.entry(*remote_ip).or_default();
228 state.origin = origin;
229 state.transport = Some(transport);
230 state.encryption = encryption;
231 }
232
233 pub fn remove_peer(&mut self, remote_ip: &SocketAddr) {
235 if let Some(state) = self.peers.get(remote_ip) {
236 self.previously_uploaded_bytes += state.upload.bytes_sent;
237 self.peers.remove(remote_ip);
238 self.seeders.remove(remote_ip);
239 self.leeches.remove(remote_ip);
240 }
241 }
242
243 pub fn get(&self, peer_ip: &SocketAddr) -> Option<&PeerState> {
245 self.peers.get(peer_ip)
246 }
247
248 pub fn seeders_count(&self) -> usize {
250 self.seeders.len()
251 }
252
253 pub fn leeches_count(&self) -> usize {
255 self.leeches.len()
256 }
257
258 pub fn leeches(&self) -> &HashSet<SocketAddr> {
260 &self.leeches
261 }
262
263 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&SocketAddr, &PeerState)> {
265 self.peers.iter()
266 }
267
268 pub fn uploaded_bytes(&self) -> usize {
271 self.previously_uploaded_bytes
272 + self.peers.values().map(|state| state.upload.bytes_sent).sum::<usize>()
273 }
274}