torrust_tracker_primitives/
peer.rs

1//! Peer struct used by the core `Tracker`.
2//!
3//! A sample peer:
4//!
5//! ```rust,no_run
6//! use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes, PeerId};
7//! use torrust_tracker_primitives::peer;
8//! use std::net::SocketAddr;
9//! use std::net::IpAddr;
10//! use std::net::Ipv4Addr;
11//! use torrust_tracker_primitives::DurationSinceUnixEpoch;
12//!
13//!
14//! peer::Peer {
15//!     peer_id: PeerId(*b"-qB00000000000000000"),
16//!     peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1)), 8080),
17//!     updated: DurationSinceUnixEpoch::new(1_669_397_478_934, 0),
18//!     uploaded: NumberOfBytes::new(0),
19//!     downloaded: NumberOfBytes::new(0),
20//!     left: NumberOfBytes::new(0),
21//!     event: AnnounceEvent::Started,
22//! };
23//! ```
24
25use 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/// Peer struct used by the core `Tracker`.
36///
37/// A sample peer:
38///
39/// ```rust,no_run
40/// use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes, PeerId};
41/// use torrust_tracker_primitives::peer;
42/// use std::net::SocketAddr;
43/// use std::net::IpAddr;
44/// use std::net::Ipv4Addr;
45/// use torrust_tracker_primitives::DurationSinceUnixEpoch;
46///
47///
48/// peer::Peer {
49///     peer_id: PeerId(*b"-qB00000000000000000"),
50///     peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1)), 8080),
51///     updated: DurationSinceUnixEpoch::new(1_669_397_478_934, 0),
52///     uploaded: NumberOfBytes::new(0),
53///     downloaded: NumberOfBytes::new(0),
54///     left: NumberOfBytes::new(0),
55///     event: AnnounceEvent::Started,
56/// };
57/// ```
58#[derive(Debug, Clone, Serialize, Copy, PartialEq, Eq, Hash)]
59pub struct Peer {
60    /// ID used by the downloader peer
61    #[serde(serialize_with = "ser_peer_id")]
62    pub peer_id: PeerId,
63    /// The IP and port this peer is listening on
64    pub peer_addr: SocketAddr,
65    /// The last time the the tracker receive an announce request from this peer (timestamp)
66    #[serde(serialize_with = "ser_unix_time_value")]
67    pub updated: DurationSinceUnixEpoch,
68    /// The total amount of bytes uploaded by this peer so far
69    #[serde(serialize_with = "ser_number_of_bytes")]
70    pub uploaded: NumberOfBytes,
71    /// The total amount of bytes downloaded by this peer so far
72    #[serde(serialize_with = "ser_number_of_bytes")]
73    pub downloaded: NumberOfBytes,
74    /// The number of bytes this peer still has to download
75    #[serde(serialize_with = "ser_number_of_bytes")]
76    pub left: NumberOfBytes,
77    /// This is an optional key which maps to started, completed, or stopped (or empty, which is the same as not being present).
78    #[serde(serialize_with = "ser_announce_event")]
79    pub event: AnnounceEvent,
80}
81
82/// Serializes a `DurationSinceUnixEpoch` as a Unix timestamp in milliseconds.
83/// # Errors
84///
85/// Will return `serde::Serializer::Error` if unable to serialize the `unix_time_value`.
86pub 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
99/// Serializes a `Announce Event` as a enum.
100///
101/// # Errors
102///
103/// If will return an error if the internal serializer was to fail.
104pub 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
115/// Serializes a `Announce Event` as a i64.
116///
117/// # Errors
118///
119/// If will return an error if the internal serializer was to fail.
120pub 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
124/// Serializes a `PeerId` as a `peer::Id`.
125///
126/// # Errors
127///
128/// If will return an error if the internal serializer was to fail.
129pub 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/// Error returned when trying to convert an invalid peer id from another type.
218///
219/// Usually because the source format does not contain 20 bytes.
220#[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    /// Converts to hex string.
312    ///
313    /// For the `PeerId` `-qB00000000000000000` it returns `2d71423030303030303030303030303030303030`
314    ///
315    /// For example:
316    ///
317    ///```text
318    /// Bytes                = Hex
319    /// -qB00000000000000000 = 2d71423030303030303030303030303030303030
320    /// -qB00000000000000000 = 2d 71 42 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
321    ///
322    /// -------------
323    /// |Char | Hex |
324    /// -------------
325    /// | -   | 2D  |
326    /// | q   | 71  |
327    /// | B   | 42  |
328    /// | 0   | 30  |
329    /// -------------
330    /// ```
331    ///
332    /// Return `None` is some of the bytes are invalid UTF8 values.
333    ///
334    /// # Panics
335    ///
336    /// It will panic if the `binascii::bin2hex` from a too-small output buffer.
337    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
375/// Marker Trait for Peer Vectors
376pub 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}