use {
super::{constants::HASH_SIZE, ChainHashError},
serde::{Deserialize, Serialize},
std::convert::TryInto,
};
#[derive(Deserialize, Serialize)]
pub struct Hash([u8; HASH_SIZE]);
impl Clone for Hash {
fn clone(&self) -> Self {
Self(*self.bytes())
}
}
impl Hash {
pub fn string(&self) -> Result<String, ChainHashError> {
let mut hash = self.0;
let mut i = 0;
while i < HASH_SIZE / 2 {
hash.swap(i, HASH_SIZE - 1 - i);
i += 1;
}
let s = hex::encode(hash);
Ok(s)
}
pub fn bytes(&self) -> &[u8; HASH_SIZE] {
&self.0
}
pub fn set_bytes(&mut self, hash: Vec<u8>) -> Result<(), ChainHashError> {
if hash.len() != HASH_SIZE {
return Err(ChainHashError::HashSize);
}
let boxed_slice = hash.into_boxed_slice();
let boxed_array: Box<[u8; HASH_SIZE]> = match boxed_slice.try_into() {
Ok(ba) => ba,
Err(_) => return Err(ChainHashError::HashSize),
};
self.0 = *boxed_array;
Ok(())
}
pub const fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn is_equal(&self, hash: &Self) -> bool {
self.0 == hash.0
}
pub fn new(hash: Vec<u8>) -> Result<Self, ChainHashError> {
let mut h = Self { 0: [0; 32] };
match h.set_bytes(hash) {
Ok(_) => {}
Err(e) => return Err(e),
};
Ok(h)
}
pub fn new_from_str(value: &str) -> Result<Hash, ChainHashError> {
let mut h = Self { 0: [0; HASH_SIZE] };
match h.decode(value) {
Ok(_) => {}
Err(e) => return Err(e),
};
Ok(h)
}
pub fn decode(&mut self, src: &str) -> Result<(), ChainHashError> {
if src.len() > super::constants::MAX_HASH_STRING_SIZE {
return Err(ChainHashError::HashStringSize);
}
let src_bytes = if src.len() % 2 == 0 {
src.as_bytes().to_vec()
} else {
let mut v = Vec::with_capacity(1 + src.len()); v.push(48);
for a in src.as_bytes().iter() {
v.push(*a);
}
v
};
let mut reversed_hash = [0; HASH_SIZE];
let src_len = src_bytes.len() / 2;
match hex::decode_to_slice(src_bytes, &mut reversed_hash[HASH_SIZE - src_len..]) {
Ok(_) => {}
Err(e) => return Err(ChainHashError::HexDecode(e)),
};
let mut i = HASH_SIZE / 2;
while i < HASH_SIZE {
self.0[i] = reversed_hash[HASH_SIZE - 1 - i];
self.0[HASH_SIZE - 1 - i] = reversed_hash[i];
i += 1;
}
Ok(())
}
}
impl std::fmt::Debug for Hash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Hash({:?})", self.bytes())
}
}