|
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