use crate::addressing::Address; use actix::prelude::*; use anyhow::{anyhow, Result}; use diesel::backend::Backend; use diesel::deserialize::FromSql; use diesel::sqlite::Sqlite; use diesel::{deserialize, sql_types}; use filebuffer::FileBuffer; use log::trace; use serde::{ser, Serialize, Serializer}; use std::path::{PathBuf, Path}; use tiny_keccak::{Hasher, KangarooTwelve}; #[derive(Debug, Clone, Eq, PartialEq, FromSqlRow, Hash)] pub struct Hash(pub Vec); impl AsRef<[u8]> for Hash { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } impl FromSql for Hash { fn from_sql(bytes: Option<&::RawValue>) -> deserialize::Result { Ok(Hash(Vec::from(not_none!(bytes).read_blob()))) } } impl Serialize for Hash { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: Serializer, { serializer.serialize_str( encode( Address::Hash(self.clone()) .encode() .map_err(ser::Error::custom)?, ) .as_str(), ) } } pub trait Hashable { fn hash(&self) -> Result; } pub struct HasherWorker; impl Actor for HasherWorker { type Context = SyncContext; } #[derive(Message)] #[rtype(result = "Result")] pub struct ComputeHash { pub path: PathBuf, } impl Handler for HasherWorker { type Result = Result; fn handle(&mut self, msg: ComputeHash, _: &mut Self::Context) -> Self::Result { msg.path.as_path().hash() } } impl Hashable for Path { fn hash(self: &Path) -> Result { trace!("Hashing {:?}...", self); let fbuffer = FileBuffer::open(self)?; trace!("Finished hashing {:?}...", self); Ok(hash(&fbuffer)) } } pub fn hash>(input: T) -> Hash { let mut k12 = KangarooTwelve::new(b""); k12.update(input.as_ref()); let mut result = [0u8; 256 / 8]; k12.finalize(&mut result); Hash(Vec::from(result)) } pub fn encode>(vec: T) -> String { // multibase base58 format!("z{}", bs58::encode(vec).into_string()) } pub fn decode>(string: T) -> Result> { let string = string.as_ref(); let (base, data) = string.split_at(1); // multibase base58 if base != "z" { Err(anyhow!(format!( "data not base58 encoded, bailing... ({})", string ))) } else { Ok(bs58::decode(data).into_vec()?) } } #[cfg(test)] mod tests { use crate::util::hash::{decode, encode}; #[test] fn test_encode_decode() { let content = "Hello, World!".as_bytes(); let encoded = encode(content); let decoded = decode(encoded); assert!(decoded.is_ok()); assert_eq!(content, decoded.unwrap()); } }