| 
1 | 1 | use std::net::{IpAddr, SocketAddr};  | 
 | 2 | +use std::panic::Location;  | 
2 | 3 | 
 
  | 
3 | 4 | use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes};  | 
4 | 5 | use serde;  | 
5 | 6 | use serde::Serialize;  | 
 | 7 | +use thiserror::Error;  | 
6 | 8 | 
 
  | 
7 | 9 | use crate::http::request::Announce;  | 
8 | 10 | use crate::protocol::clock::{Current, DurationSinceUnixEpoch, Time};  | 
@@ -91,6 +93,61 @@ impl Peer {  | 
91 | 93 | #[derive(PartialEq, Eq, Hash, Clone, Debug, PartialOrd, Ord, Copy)]  | 
92 | 94 | pub struct Id(pub [u8; 20]);  | 
93 | 95 | 
 
  | 
 | 96 | +const INFO_HASH_BYTES_LEN: usize = 20;  | 
 | 97 | + | 
 | 98 | +#[derive(Error, Debug)]  | 
 | 99 | +pub enum IdConversionError {  | 
 | 100 | +    #[error("not enough bytes for infohash: {message} {location}")]  | 
 | 101 | +    NotEnoughBytes {  | 
 | 102 | +        location: &'static Location<'static>,  | 
 | 103 | +        message: String,  | 
 | 104 | +    },  | 
 | 105 | +    #[error("too many bytes for infohash: {message} {location}")]  | 
 | 106 | +    TooManyBytes {  | 
 | 107 | +        location: &'static Location<'static>,  | 
 | 108 | +        message: String,  | 
 | 109 | +    },  | 
 | 110 | +}  | 
 | 111 | + | 
 | 112 | +impl Id {  | 
 | 113 | +    /// # Panics  | 
 | 114 | +    ///  | 
 | 115 | +    /// Will panic if byte slice does not contains the exact amount of bytes need for the `Id`.  | 
 | 116 | +    #[must_use]  | 
 | 117 | +    pub fn from_bytes(bytes: &[u8]) -> Self {  | 
 | 118 | +        assert_eq!(bytes.len(), INFO_HASH_BYTES_LEN);  | 
 | 119 | +        let mut ret = Id([0u8; INFO_HASH_BYTES_LEN]);  | 
 | 120 | +        ret.0.clone_from_slice(bytes);  | 
 | 121 | +        ret  | 
 | 122 | +    }  | 
 | 123 | +}  | 
 | 124 | + | 
 | 125 | +impl From<[u8; 20]> for Id {  | 
 | 126 | +    fn from(bytes: [u8; 20]) -> Self {  | 
 | 127 | +        Id(bytes)  | 
 | 128 | +    }  | 
 | 129 | +}  | 
 | 130 | + | 
 | 131 | +impl TryFrom<Vec<u8>> for Id {  | 
 | 132 | +    type Error = IdConversionError;  | 
 | 133 | + | 
 | 134 | +    fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {  | 
 | 135 | +        if bytes.len() < INFO_HASH_BYTES_LEN {  | 
 | 136 | +            return Err(IdConversionError::NotEnoughBytes {  | 
 | 137 | +                location: Location::caller(),  | 
 | 138 | +                message: format! {"got {} bytes, expected {}", bytes.len(), INFO_HASH_BYTES_LEN},  | 
 | 139 | +            });  | 
 | 140 | +        }  | 
 | 141 | +        if bytes.len() > INFO_HASH_BYTES_LEN {  | 
 | 142 | +            return Err(IdConversionError::TooManyBytes {  | 
 | 143 | +                location: Location::caller(),  | 
 | 144 | +                message: format! {"got {} bytes, expected {}", bytes.len(), INFO_HASH_BYTES_LEN},  | 
 | 145 | +            });  | 
 | 146 | +        }  | 
 | 147 | +        Ok(Self::from_bytes(&bytes))  | 
 | 148 | +    }  | 
 | 149 | +}  | 
 | 150 | + | 
94 | 151 | impl std::fmt::Display for Id {  | 
95 | 152 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {  | 
96 | 153 |         match self.to_hex_string() {  | 
@@ -239,6 +296,75 @@ mod test {  | 
239 | 296 |     mod torrent_peer_id {  | 
240 | 297 |         use crate::tracker::peer;  | 
241 | 298 | 
 
  | 
 | 299 | +        #[test]  | 
 | 300 | +        fn should_be_instantiated_from_a_byte_slice() {  | 
 | 301 | +            let id = peer::Id::from_bytes(&[  | 
 | 302 | +                0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150,  | 
 | 303 | +            ]);  | 
 | 304 | + | 
 | 305 | +            let expected_id = peer::Id([  | 
 | 306 | +                0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150,  | 
 | 307 | +            ]);  | 
 | 308 | + | 
 | 309 | +            assert_eq!(id, expected_id);  | 
 | 310 | +        }  | 
 | 311 | + | 
 | 312 | +        #[test]  | 
 | 313 | +        #[should_panic]  | 
 | 314 | +        fn should_fail_trying_to_instantiate_from_a_byte_slice_with_less_than_20_bytes() {  | 
 | 315 | +            let less_than_20_bytes = [0; 19];  | 
 | 316 | +            let _ = peer::Id::from_bytes(&less_than_20_bytes);  | 
 | 317 | +        }  | 
 | 318 | + | 
 | 319 | +        #[test]  | 
 | 320 | +        #[should_panic]  | 
 | 321 | +        fn should_fail_trying_to_instantiate_from_a_byte_slice_with_more_than_20_bytes() {  | 
 | 322 | +            let more_than_20_bytes = [0; 21];  | 
 | 323 | +            let _ = peer::Id::from_bytes(&more_than_20_bytes);  | 
 | 324 | +        }  | 
 | 325 | + | 
 | 326 | +        #[test]  | 
 | 327 | +        fn should_be_converted_from_a_20_byte_array() {  | 
 | 328 | +            let id = peer::Id::from([  | 
 | 329 | +                0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150,  | 
 | 330 | +            ]);  | 
 | 331 | + | 
 | 332 | +            let expected_id = peer::Id([  | 
 | 333 | +                0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150,  | 
 | 334 | +            ]);  | 
 | 335 | + | 
 | 336 | +            assert_eq!(id, expected_id);  | 
 | 337 | +        }  | 
 | 338 | + | 
 | 339 | +        #[test]  | 
 | 340 | +        fn should_be_converted_from_a_byte_vector() {  | 
 | 341 | +            let id = peer::Id::try_from(  | 
 | 342 | +                [  | 
 | 343 | +                    0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150,  | 
 | 344 | +                ]  | 
 | 345 | +                .to_vec(),  | 
 | 346 | +            )  | 
 | 347 | +            .unwrap();  | 
 | 348 | + | 
 | 349 | +            let expected_id = peer::Id([  | 
 | 350 | +                0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150, 0, 159, 146, 150,  | 
 | 351 | +            ]);  | 
 | 352 | + | 
 | 353 | +            assert_eq!(id, expected_id);  | 
 | 354 | +        }  | 
 | 355 | + | 
 | 356 | +        #[test]  | 
 | 357 | +        #[should_panic]  | 
 | 358 | +        fn should_fail_trying_to_convert_from_a_byte_vector_with_less_than_20_bytes() {  | 
 | 359 | +            let _ = peer::Id::try_from([0; 19].to_vec()).unwrap();  | 
 | 360 | +        }  | 
 | 361 | + | 
 | 362 | +        #[test]  | 
 | 363 | +        #[should_panic]  | 
 | 364 | +        fn should_fail_trying_to_convert_from_a_byte_vector_with_more_than_20_bytes() {  | 
 | 365 | +            let _ = peer::Id::try_from([0; 21].to_vec()).unwrap();  | 
 | 366 | +        }  | 
 | 367 | + | 
242 | 368 |         #[test]  | 
243 | 369 |         fn should_be_converted_to_hex_string() {  | 
244 | 370 |             let id = peer::Id(*b"-qB00000000000000000");  | 
 | 
0 commit comments