use std::hash::{DefaultHasher, Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::panic::Location;
use thiserror::Error;
use zerocopy::FromBytes;
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
pub struct InfoHash {
data: aquatic_udp_protocol::InfoHash,
}
pub const INFO_HASH_BYTES_LEN: usize = 20;
impl InfoHash {
#[must_use]
pub fn from_bytes(bytes: &[u8]) -> Self {
let data = aquatic_udp_protocol::InfoHash::read_from(bytes).expect("it should have the exact amount of bytes");
Self { data }
}
#[must_use]
pub fn bytes(&self) -> [u8; 20] {
self.0
}
#[must_use]
pub fn to_hex_string(&self) -> String {
self.to_string()
}
}
impl Default for InfoHash {
fn default() -> Self {
Self {
data: aquatic_udp_protocol::InfoHash(Default::default()),
}
}
}
impl From<aquatic_udp_protocol::InfoHash> for InfoHash {
fn from(data: aquatic_udp_protocol::InfoHash) -> Self {
Self { data }
}
}
impl Deref for InfoHash {
type Target = aquatic_udp_protocol::InfoHash;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl DerefMut for InfoHash {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl Ord for InfoHash {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl PartialOrd<InfoHash> for InfoHash {
fn partial_cmp(&self, other: &InfoHash) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl std::fmt::Display for InfoHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut chars = [0u8; 40];
binascii::bin2hex(&self.0, &mut chars).expect("failed to hexlify");
write!(f, "{}", std::str::from_utf8(&chars).unwrap())
}
}
impl std::str::FromStr for InfoHash {
type Err = binascii::ConvertError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut i = Self::default();
if s.len() != 40 {
return Err(binascii::ConvertError::InvalidInputLength);
}
binascii::hex2bin(s.as_bytes(), &mut i.0)?;
Ok(i)
}
}
impl std::convert::From<&[u8]> for InfoHash {
fn from(data: &[u8]) -> InfoHash {
assert_eq!(data.len(), 20);
let mut ret = Self::default();
ret.0.clone_from_slice(data);
ret
}
}
impl std::convert::From<&DefaultHasher> for InfoHash {
fn from(data: &DefaultHasher) -> InfoHash {
let n = data.finish().to_le_bytes();
let bytes = [
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],
n[3],
];
let data = aquatic_udp_protocol::InfoHash(bytes);
Self { data }
}
}
impl std::convert::From<&i32> for InfoHash {
fn from(n: &i32) -> InfoHash {
let n = n.to_le_bytes();
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]];
let data = aquatic_udp_protocol::InfoHash(bytes);
Self { data }
}
}
impl std::convert::From<[u8; 20]> for InfoHash {
fn from(bytes: [u8; 20]) -> Self {
let data = aquatic_udp_protocol::InfoHash(bytes);
Self { data }
}
}
#[derive(Error, Debug)]
pub enum ConversionError {
#[error("not enough bytes for infohash: {message} {location}")]
NotEnoughBytes {
location: &'static Location<'static>,
message: String,
},
#[error("too many bytes for infohash: {message} {location}")]
TooManyBytes {
location: &'static Location<'static>,
message: String,
},
}
impl TryFrom<Vec<u8>> for InfoHash {
type Error = ConversionError;
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
if bytes.len() < INFO_HASH_BYTES_LEN {
return Err(ConversionError::NotEnoughBytes {
location: Location::caller(),
message: format! {"got {} bytes, expected {}", bytes.len(), INFO_HASH_BYTES_LEN},
});
}
if bytes.len() > INFO_HASH_BYTES_LEN {
return Err(ConversionError::TooManyBytes {
location: Location::caller(),
message: format! {"got {} bytes, expected {}", bytes.len(), INFO_HASH_BYTES_LEN},
});
}
Ok(Self::from_bytes(&bytes))
}
}
impl serde::ser::Serialize for InfoHash {
fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut buffer = [0u8; 40];
let bytes_out = binascii::bin2hex(&self.0, &mut buffer).ok().unwrap();
let str_out = std::str::from_utf8(bytes_out).unwrap();
serializer.serialize_str(str_out)
}
}
impl<'de> serde::de::Deserialize<'de> for InfoHash {
fn deserialize<D: serde::de::Deserializer<'de>>(des: D) -> Result<Self, D::Error> {
des.deserialize_str(InfoHashVisitor)
}
}
struct InfoHashVisitor;
impl<'v> serde::de::Visitor<'v> for InfoHashVisitor {
type Value = InfoHash;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "a 40 character long hash")
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
if v.len() != 40 {
return Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(v),
&"a 40 character long string",
));
}
let mut res = InfoHash::default();
if binascii::hex2bin(v.as_bytes(), &mut res.0).is_err() {
return Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(v),
&"a hexadecimal string",
));
};
Ok(res)
}
}