use std::collections::VecDeque;
use std::fmt::{self, Display};
use std::future::Future;
use std::pin::Pin;
use futures_util::future::FutureExt;
use libp2p_core::multihash::Multihash;
use multihash_codetable::MultihashDigest;
use crate::utils::convert_multihash;
#[derive(Debug, thiserror::Error)]
pub enum MultihasherError {
#[error("Unknown multihash code")]
UnknownMultihashCode,
#[error("Invalid multihash size")]
InvalidMultihashSize,
#[error("Hashing failure: {0}")]
Custom(String),
#[error("Fatal hashing failure: {0}")]
CustomFatal(String),
}
impl MultihasherError {
pub fn custom(e: impl Display) -> MultihasherError {
MultihasherError::Custom(e.to_string())
}
pub fn custom_fatal(e: impl Display) -> MultihasherError {
MultihasherError::CustomFatal(e.to_string())
}
}
pub trait Multihasher<const S: usize> {
fn hash(
&self,
multihash_code: u64,
input: &[u8],
) -> impl Future<Output = Result<Multihash<S>, MultihasherError>> + Send;
}
trait DispatchableMultihasher<const S: usize> {
fn hash<'a>(
&'a self,
multihash_code: u64,
input: &'a [u8],
) -> Pin<Box<dyn Future<Output = Result<Multihash<S>, MultihasherError>> + Send + 'a>>;
}
impl<const S: usize, T> DispatchableMultihasher<S> for T
where
T: Multihasher<S>,
{
fn hash<'a>(
&'a self,
multihash_code: u64,
input: &'a [u8],
) -> Pin<Box<dyn Future<Output = Result<Multihash<S>, MultihasherError>> + Send + 'a>> {
Multihasher::<S>::hash(self, multihash_code, input).boxed()
}
}
pub struct StandardMultihasher;
impl<const S: usize> Multihasher<S> for StandardMultihasher {
async fn hash(
&self,
multihash_code: u64,
input: &[u8],
) -> Result<Multihash<S>, MultihasherError> {
let hasher = multihash_codetable::Code::try_from(multihash_code)
.map_err(|_| MultihasherError::UnknownMultihashCode)?;
let hash = hasher.digest(input);
convert_multihash(&hash).ok_or(MultihasherError::InvalidMultihashSize)
}
}
pub(crate) struct MultihasherTable<const S: usize> {
multihashers: VecDeque<Box<dyn DispatchableMultihasher<S> + Send + Sync + 'static>>,
}
impl<const S: usize> fmt::Debug for MultihasherTable<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("MultihasherTable { .. }")
}
}
impl<const S: usize> MultihasherTable<S> {
pub(crate) fn new() -> Self {
let mut table = MultihasherTable {
multihashers: VecDeque::new(),
};
table.register(StandardMultihasher);
table
}
pub(crate) fn register<M>(&mut self, multihasher: M)
where
M: Multihasher<S> + Send + Sync + 'static,
{
self.multihashers.push_front(Box::new(multihasher));
}
pub(crate) async fn hash(
&self,
multihash_code: u64,
input: &[u8],
) -> Result<Multihash<S>, MultihasherError> {
for multihasher in &self.multihashers {
match multihasher.hash(multihash_code, input).await {
Ok(hash) => return Ok(hash),
Err(MultihasherError::UnknownMultihashCode) => continue,
Err(e) => return Err(e),
}
}
Err(MultihasherError::UnknownMultihashCode)
}
}