torrust_tracker_primitives/
info_hash.rs1use std::hash::{DefaultHasher, Hash, Hasher};
2use std::ops::{Deref, DerefMut};
3use std::panic::Location;
4
5use thiserror::Error;
6use zerocopy::FromBytes;
7
8#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
10pub struct InfoHash {
11 data: aquatic_udp_protocol::InfoHash,
12}
13
14pub const INFO_HASH_BYTES_LEN: usize = 20;
15
16impl InfoHash {
17 #[must_use]
23 pub fn from_bytes(bytes: &[u8]) -> Self {
24 let data = aquatic_udp_protocol::InfoHash::read_from(bytes).expect("it should have the exact amount of bytes");
25
26 Self { data }
27 }
28
29 #[must_use]
31 pub fn bytes(&self) -> [u8; 20] {
32 self.0
33 }
34
35 #[must_use]
37 pub fn to_hex_string(&self) -> String {
38 self.to_string()
39 }
40}
41
42impl Default for InfoHash {
43 fn default() -> Self {
44 Self {
45 data: aquatic_udp_protocol::InfoHash(Default::default()),
46 }
47 }
48}
49
50impl From<aquatic_udp_protocol::InfoHash> for InfoHash {
51 fn from(data: aquatic_udp_protocol::InfoHash) -> Self {
52 Self { data }
53 }
54}
55
56impl Deref for InfoHash {
57 type Target = aquatic_udp_protocol::InfoHash;
58
59 fn deref(&self) -> &Self::Target {
60 &self.data
61 }
62}
63
64impl DerefMut for InfoHash {
65 fn deref_mut(&mut self) -> &mut Self::Target {
66 &mut self.data
67 }
68}
69
70impl Ord for InfoHash {
71 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
72 self.0.cmp(&other.0)
73 }
74}
75
76impl PartialOrd<InfoHash> for InfoHash {
77 fn partial_cmp(&self, other: &InfoHash) -> Option<std::cmp::Ordering> {
78 Some(self.cmp(other))
79 }
80}
81
82impl std::fmt::Display for InfoHash {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 let mut chars = [0u8; 40];
85 binascii::bin2hex(&self.0, &mut chars).expect("failed to hexlify");
86 write!(f, "{}", std::str::from_utf8(&chars).unwrap())
87 }
88}
89
90impl std::str::FromStr for InfoHash {
91 type Err = binascii::ConvertError;
92
93 fn from_str(s: &str) -> Result<Self, Self::Err> {
94 let mut i = Self::default();
95 if s.len() != 40 {
96 return Err(binascii::ConvertError::InvalidInputLength);
97 }
98 binascii::hex2bin(s.as_bytes(), &mut i.0)?;
99 Ok(i)
100 }
101}
102
103impl std::convert::From<&[u8]> for InfoHash {
104 fn from(data: &[u8]) -> InfoHash {
105 assert_eq!(data.len(), 20);
106 let mut ret = Self::default();
107 ret.0.clone_from_slice(data);
108 ret
109 }
110}
111
112impl std::convert::From<&DefaultHasher> for InfoHash {
114 fn from(data: &DefaultHasher) -> InfoHash {
115 let n = data.finish().to_le_bytes();
116 let bytes = [
117 n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[0], n[1], n[2],
118 n[3],
119 ];
120 let data = aquatic_udp_protocol::InfoHash(bytes);
121 Self { data }
122 }
123}
124
125impl std::convert::From<&i32> for InfoHash {
126 fn from(n: &i32) -> InfoHash {
127 let n = n.to_le_bytes();
128 let bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, n[0], n[1], n[2], n[3]];
129 let data = aquatic_udp_protocol::InfoHash(bytes);
130 Self { data }
131 }
132}
133
134impl std::convert::From<[u8; 20]> for InfoHash {
135 fn from(bytes: [u8; 20]) -> Self {
136 let data = aquatic_udp_protocol::InfoHash(bytes);
137 Self { data }
138 }
139}
140
141#[derive(Error, Debug)]
143pub enum ConversionError {
144 #[error("not enough bytes for infohash: {message} {location}")]
146 NotEnoughBytes {
147 location: &'static Location<'static>,
148 message: String,
149 },
150 #[error("too many bytes for infohash: {message} {location}")]
152 TooManyBytes {
153 location: &'static Location<'static>,
154 message: String,
155 },
156}
157
158impl TryFrom<Vec<u8>> for InfoHash {
159 type Error = ConversionError;
160
161 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
162 if bytes.len() < INFO_HASH_BYTES_LEN {
163 return Err(ConversionError::NotEnoughBytes {
164 location: Location::caller(),
165 message: format! {"got {} bytes, expected {}", bytes.len(), INFO_HASH_BYTES_LEN},
166 });
167 }
168 if bytes.len() > INFO_HASH_BYTES_LEN {
169 return Err(ConversionError::TooManyBytes {
170 location: Location::caller(),
171 message: format! {"got {} bytes, expected {}", bytes.len(), INFO_HASH_BYTES_LEN},
172 });
173 }
174 Ok(Self::from_bytes(&bytes))
175 }
176}
177
178impl serde::ser::Serialize for InfoHash {
179 fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
180 let mut buffer = [0u8; 40];
181 let bytes_out = binascii::bin2hex(&self.0, &mut buffer).ok().unwrap();
182 let str_out = std::str::from_utf8(bytes_out).unwrap();
183 serializer.serialize_str(str_out)
184 }
185}
186
187impl<'de> serde::de::Deserialize<'de> for InfoHash {
188 fn deserialize<D: serde::de::Deserializer<'de>>(des: D) -> Result<Self, D::Error> {
189 des.deserialize_str(InfoHashVisitor)
190 }
191}
192
193struct InfoHashVisitor;
194
195impl<'v> serde::de::Visitor<'v> for InfoHashVisitor {
196 type Value = InfoHash;
197
198 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 write!(formatter, "a 40 character long hash")
200 }
201
202 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
203 if v.len() != 40 {
204 return Err(serde::de::Error::invalid_value(
205 serde::de::Unexpected::Str(v),
206 &"a 40 character long string",
207 ));
208 }
209
210 let mut res = InfoHash::default();
211
212 if binascii::hex2bin(v.as_bytes(), &mut res.0).is_err() {
213 return Err(serde::de::Error::invalid_value(
214 serde::de::Unexpected::Str(v),
215 &"a hexadecimal string",
216 ));
217 };
218 Ok(res)
219 }
220}