1use std::net::{IpAddr, SocketAddr};
26use std::ops::{Deref, DerefMut};
27use std::sync::Arc;
28
29use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes, PeerId};
30use serde::Serialize;
31use zerocopy::FromBytes as _;
32
33use crate::DurationSinceUnixEpoch;
34
35#[derive(Debug, Clone, Serialize, Copy, PartialEq, Eq, Hash)]
59pub struct Peer {
60 #[serde(serialize_with = "ser_peer_id")]
62 pub peer_id: PeerId,
63 pub peer_addr: SocketAddr,
65 #[serde(serialize_with = "ser_unix_time_value")]
67 pub updated: DurationSinceUnixEpoch,
68 #[serde(serialize_with = "ser_number_of_bytes")]
70 pub uploaded: NumberOfBytes,
71 #[serde(serialize_with = "ser_number_of_bytes")]
73 pub downloaded: NumberOfBytes,
74 #[serde(serialize_with = "ser_number_of_bytes")]
76 pub left: NumberOfBytes,
77 #[serde(serialize_with = "ser_announce_event")]
79 pub event: AnnounceEvent,
80}
81
82pub fn ser_unix_time_value<S: serde::Serializer>(unix_time_value: &DurationSinceUnixEpoch, ser: S) -> Result<S::Ok, S::Error> {
87 #[allow(clippy::cast_possible_truncation)]
88 ser.serialize_u64(unix_time_value.as_millis() as u64)
89}
90
91#[derive(Serialize)]
92pub enum AnnounceEventSer {
93 Started,
94 Stopped,
95 Completed,
96 None,
97}
98
99pub fn ser_announce_event<S: serde::Serializer>(announce_event: &AnnounceEvent, ser: S) -> Result<S::Ok, S::Error> {
105 let event_ser = match announce_event {
106 AnnounceEvent::Started => AnnounceEventSer::Started,
107 AnnounceEvent::Stopped => AnnounceEventSer::Stopped,
108 AnnounceEvent::Completed => AnnounceEventSer::Completed,
109 AnnounceEvent::None => AnnounceEventSer::None,
110 };
111
112 ser.serialize_some(&event_ser)
113}
114
115pub fn ser_number_of_bytes<S: serde::Serializer>(number_of_bytes: &NumberOfBytes, ser: S) -> Result<S::Ok, S::Error> {
121 ser.serialize_i64(number_of_bytes.0.get())
122}
123
124pub fn ser_peer_id<S: serde::Serializer>(peer_id: &PeerId, ser: S) -> Result<S::Ok, S::Error> {
130 let id = Id { data: *peer_id };
131 ser.serialize_some(&id)
132}
133
134impl Ord for Peer {
135 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
136 self.peer_id.cmp(&other.peer_id)
137 }
138}
139
140impl PartialOrd for Peer {
141 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
142 Some(self.peer_id.cmp(&other.peer_id))
143 }
144}
145
146pub trait ReadInfo {
147 fn is_seeder(&self) -> bool;
148 fn get_event(&self) -> AnnounceEvent;
149 fn get_id(&self) -> PeerId;
150 fn get_updated(&self) -> DurationSinceUnixEpoch;
151 fn get_address(&self) -> SocketAddr;
152}
153
154impl ReadInfo for Peer {
155 fn is_seeder(&self) -> bool {
156 self.left.0.get() <= 0 && self.event != AnnounceEvent::Stopped
157 }
158
159 fn get_event(&self) -> AnnounceEvent {
160 self.event
161 }
162
163 fn get_id(&self) -> PeerId {
164 self.peer_id
165 }
166
167 fn get_updated(&self) -> DurationSinceUnixEpoch {
168 self.updated
169 }
170
171 fn get_address(&self) -> SocketAddr {
172 self.peer_addr
173 }
174}
175
176impl ReadInfo for Arc<Peer> {
177 fn is_seeder(&self) -> bool {
178 self.left.0.get() <= 0 && self.event != AnnounceEvent::Stopped
179 }
180
181 fn get_event(&self) -> AnnounceEvent {
182 self.event
183 }
184
185 fn get_id(&self) -> PeerId {
186 self.peer_id
187 }
188
189 fn get_updated(&self) -> DurationSinceUnixEpoch {
190 self.updated
191 }
192
193 fn get_address(&self) -> SocketAddr {
194 self.peer_addr
195 }
196}
197
198impl Peer {
199 #[must_use]
200 pub fn is_seeder(&self) -> bool {
201 self.left.0.get() <= 0 && self.event != AnnounceEvent::Stopped
202 }
203
204 pub fn ip(&mut self) -> IpAddr {
205 self.peer_addr.ip()
206 }
207
208 pub fn change_ip(&mut self, new_ip: &IpAddr) {
209 self.peer_addr = SocketAddr::new(*new_ip, self.peer_addr.port());
210 }
211}
212
213use std::panic::Location;
214
215use thiserror::Error;
216
217#[derive(Error, Debug)]
221pub enum IdConversionError {
222 #[error("not enough bytes for peer id: {message} {location}")]
223 NotEnoughBytes {
224 location: &'static Location<'static>,
225 message: String,
226 },
227 #[error("too many bytes for peer id: {message} {location}")]
228 TooManyBytes {
229 location: &'static Location<'static>,
230 message: String,
231 },
232}
233
234pub struct Id {
235 data: PeerId,
236}
237
238impl From<PeerId> for Id {
239 fn from(id: PeerId) -> Self {
240 Self { data: id }
241 }
242}
243
244impl Deref for Id {
245 type Target = PeerId;
246
247 fn deref(&self) -> &Self::Target {
248 &self.data
249 }
250}
251
252impl DerefMut for Id {
253 fn deref_mut(&mut self) -> &mut Self::Target {
254 &mut self.data
255 }
256}
257
258impl Id {
259 #[must_use]
260 pub fn new<T>(number: T) -> Self
261 where
262 T: Into<i128>,
263 {
264 let number: i128 = number.into();
265 let number = number.to_le_bytes();
266 let bytes = [
267 0u8, 0u8, 0u8, 0u8, number[0], number[1], number[2], number[3], number[4], number[5], number[6], number[7],
268 number[8], number[9], number[10], number[11], number[12], number[13], number[14], number[15],
269 ];
270
271 let data = PeerId(bytes);
272 Id { data }
273 }
274}
275
276impl TryFrom<Vec<u8>> for Id {
277 type Error = IdConversionError;
278
279 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
280 if bytes.len() < PEER_ID_BYTES_LEN {
281 return Err(IdConversionError::NotEnoughBytes {
282 location: Location::caller(),
283 message: format! {"got {} bytes, expected {}", bytes.len(), PEER_ID_BYTES_LEN},
284 });
285 }
286 if bytes.len() > PEER_ID_BYTES_LEN {
287 return Err(IdConversionError::TooManyBytes {
288 location: Location::caller(),
289 message: format! {"got {} bytes, expected {}", bytes.len(), PEER_ID_BYTES_LEN},
290 });
291 }
292
293 let data = PeerId::read_from(&bytes).expect("it should have the correct amount of bytes");
294 Ok(Self { data })
295 }
296}
297
298impl std::fmt::Display for Id {
299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300 match self.to_hex_string() {
301 Some(hex) => write!(f, "{hex}"),
302 None => write!(f, ""),
303 }
304 }
305}
306
307pub const PEER_ID_BYTES_LEN: usize = 20;
308
309impl Id {
310 #[must_use]
311 pub fn to_hex_string(&self) -> Option<String> {
338 let buff_size = self.0.len() * 2;
339 let mut tmp: Vec<u8> = vec![0; buff_size];
340
341 binascii::bin2hex(&self.0, &mut tmp).unwrap();
342
343 match std::str::from_utf8(&tmp) {
344 Ok(hex) => Some(format!("0x{hex}")),
345 Err(_) => None,
346 }
347 }
348
349 #[must_use]
350 pub fn get_client_name(&self) -> Option<String> {
351 let peer_id = tdyne_peer_id::PeerId::from(self.0);
352 tdyne_peer_id_registry::parse(peer_id).ok().map(|parsed| parsed.client)
353 }
354}
355
356impl Serialize for Id {
357 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
358 where
359 S: serde::Serializer,
360 {
361 #[derive(Serialize)]
362 struct PeerIdInfo {
363 id: Option<String>,
364 client: Option<String>,
365 }
366
367 let obj = PeerIdInfo {
368 id: self.to_hex_string(),
369 client: self.get_client_name(),
370 };
371 obj.serialize(serializer)
372 }
373}
374
375pub trait Encoding: From<Peer> + PartialEq {}
377
378impl<P: Encoding> FromIterator<Peer> for Vec<P> {
379 fn from_iter<T: IntoIterator<Item = Peer>>(iter: T) -> Self {
380 let mut peers: Vec<P> = vec![];
381
382 for peer in iter {
383 peers.push(peer.into());
384 }
385
386 peers
387 }
388}
389
390pub mod fixture {
391 use std::net::{IpAddr, Ipv4Addr, SocketAddr};
392
393 use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes};
394
395 use super::{Id, Peer, PeerId};
396 use crate::DurationSinceUnixEpoch;
397
398 #[derive(PartialEq, Debug)]
399
400 pub struct PeerBuilder {
401 peer: Peer,
402 }
403
404 #[allow(clippy::derivable_impls)]
405 impl Default for PeerBuilder {
406 fn default() -> Self {
407 Self { peer: Peer::default() }
408 }
409 }
410
411 impl PeerBuilder {
412 #[allow(dead_code)]
413 #[must_use]
414 pub fn seeder() -> Self {
415 let peer = Peer {
416 peer_id: PeerId(*b"-qB00000000000000001"),
417 peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080),
418 updated: DurationSinceUnixEpoch::new(1_669_397_478_934, 0),
419 uploaded: NumberOfBytes::new(0),
420 downloaded: NumberOfBytes::new(0),
421 left: NumberOfBytes::new(0),
422 event: AnnounceEvent::Completed,
423 };
424
425 Self { peer }
426 }
427
428 #[allow(dead_code)]
429 #[must_use]
430 pub fn leecher() -> Self {
431 let peer = Peer {
432 peer_id: PeerId(*b"-qB00000000000000002"),
433 peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 8080),
434 updated: DurationSinceUnixEpoch::new(1_669_397_478_934, 0),
435 uploaded: NumberOfBytes::new(0),
436 downloaded: NumberOfBytes::new(0),
437 left: NumberOfBytes::new(10),
438 event: AnnounceEvent::Started,
439 };
440
441 Self { peer }
442 }
443
444 #[allow(dead_code)]
445 #[must_use]
446 pub fn with_peer_id(mut self, peer_id: &PeerId) -> Self {
447 self.peer.peer_id = *peer_id;
448 self
449 }
450
451 #[allow(dead_code)]
452 #[must_use]
453 pub fn with_peer_addr(mut self, peer_addr: &SocketAddr) -> Self {
454 self.peer.peer_addr = *peer_addr;
455 self
456 }
457
458 #[allow(dead_code)]
459 #[must_use]
460 pub fn with_bytes_pending_to_download(mut self, left: i64) -> Self {
461 self.peer.left = NumberOfBytes::new(left);
462 self
463 }
464
465 #[allow(dead_code)]
466 #[must_use]
467 pub fn with_no_bytes_pending_to_download(mut self) -> Self {
468 self.peer.left = NumberOfBytes::new(0);
469 self
470 }
471
472 #[allow(dead_code)]
473 #[must_use]
474 pub fn last_updated_on(mut self, updated: DurationSinceUnixEpoch) -> Self {
475 self.peer.updated = updated;
476 self
477 }
478
479 #[allow(dead_code)]
480 #[must_use]
481 pub fn build(self) -> Peer {
482 self.into()
483 }
484
485 #[allow(dead_code)]
486 #[must_use]
487 pub fn into(self) -> Peer {
488 self.peer
489 }
490 }
491
492 impl Default for Peer {
493 fn default() -> Self {
494 Self {
495 peer_id: PeerId(*b"-qB00000000000000000"),
496 peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080),
497 updated: DurationSinceUnixEpoch::new(1_669_397_478_934, 0),
498 uploaded: NumberOfBytes::new(0),
499 downloaded: NumberOfBytes::new(0),
500 left: NumberOfBytes::new(0),
501 event: AnnounceEvent::Started,
502 }
503 }
504 }
505
506 impl Default for Id {
507 fn default() -> Self {
508 let data = PeerId(*b"-qB00000000000000000");
509 Self { data }
510 }
511 }
512}
513
514#[cfg(test)]
515pub mod test {
516 mod torrent_peer_id {
517 use aquatic_udp_protocol::PeerId;
518
519 use crate::peer;
520
521 #[test]
522 #[should_panic = "NotEnoughBytes"]
523 fn should_fail_trying_to_convert_from_a_byte_vector_with_less_than_20_bytes() {
524 let _ = peer::Id::try_from([0; 19].to_vec()).unwrap();
525 }
526
527 #[test]
528 #[should_panic = "TooManyBytes"]
529 fn should_fail_trying_to_convert_from_a_byte_vector_with_more_than_20_bytes() {
530 let _ = peer::Id::try_from([0; 21].to_vec()).unwrap();
531 }
532
533 #[test]
534 fn should_be_converted_to_hex_string() {
535 let id = peer::Id {
536 data: PeerId(*b"-qB00000000000000000"),
537 };
538 assert_eq!(id.to_hex_string().unwrap(), "0x2d71423030303030303030303030303030303030");
539
540 let id = peer::Id {
541 data: PeerId([
542 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150,
543 ]),
544 };
545 assert_eq!(id.to_hex_string().unwrap(), "0x009f9296009f9296009f9296009f9296009f9296");
546 }
547
548 #[test]
549 fn should_be_converted_into_string_type_using_the_hex_string_format() {
550 let id = peer::Id {
551 data: PeerId(*b"-qB00000000000000000"),
552 };
553 assert_eq!(id.to_string(), "0x2d71423030303030303030303030303030303030");
554
555 let id = peer::Id {
556 data: PeerId([
557 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150,
558 ]),
559 };
560 assert_eq!(id.to_string(), "0x009f9296009f9296009f9296009f9296009f9296");
561 }
562 }
563}