unobtanium 3.0.0

Opinioated Web search engine library with crawler and viewer companion.
Documentation
use blake2::Blake2b512;
use blake2::Digest;
use rusqlite::types::FromSql;
use rusqlite::types::FromSqlError;
use rusqlite::types::FromSqlResult;
use rusqlite::types::ToSql;
use rusqlite::types::ToSqlOutput;
use rusqlite::types::ValueRef;
use thiserror::Error;

use std::ops::Deref;
use std::ops::DerefMut;

/// Wrapper type to hold a digest produced by a Blake2b512 algorithm.
///
/// It is 64 bytes in size.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Blake2b512Digest(Box<[u8; 64]>);

// Implement deref to byte array to allow doing things with the bytes

impl Deref for Blake2b512Digest {
	type Target = [u8; 64];

	fn deref(&self) -> &Self::Target {
		&self.0
	}
}

impl DerefMut for Blake2b512Digest {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.0
	}
}

// Make it easy to convert to a boxed slice

impl From<Blake2b512Digest> for Box<[u8]> {
	fn from(value: Blake2b512Digest) -> Self {
	    Box::new(*value)
	}
}

// Allow converting directly from a Blake2b512 hasher

impl From<Blake2b512> for Blake2b512Digest {
	fn from(value: Blake2b512) -> Self {
	    let mut buffer: [u8; 64] = [0; 64];
	    value.finalize_into((&mut buffer).into());
	    return Self(Box::new(buffer));
	}
}

// Convert other blobs to a digest

impl TryFrom<Box<[u8]>> for Blake2b512Digest {
	type Error = Blake2b512BlobLengthMismatch;
	
	fn try_from(value: Box<[u8]>) -> Result<Self, Self::Error> {
		let size = value.len();
		Ok(Self(value.try_into().map_err(|_| Blake2b512BlobLengthMismatch{ got: size })?))
	}
	
}

impl TryFrom<&[u8]> for Blake2b512Digest {
	type Error = Blake2b512BlobLengthMismatch;
	
	fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
		let size = value.len();
		Ok(Self(Box::new(value.try_into().map_err(|_| Blake2b512BlobLengthMismatch{ got: size })?)))
	}
	
}

#[derive(Debug, Clone, Error)]
#[error("Problem reading Blake2b512 hash: exepected 64 bytes, got {got} bytes")]
pub struct Blake2b512BlobLengthMismatch {
	got: usize
}

// Integrrate with rusqlite

impl FromSql for Blake2b512Digest {
	fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
	    value.as_blob()?
		    .try_into()
		    .map_err(|e: Blake2b512BlobLengthMismatch| FromSqlError::InvalidBlobSize {
			    expected_size: 64,
			    blob_size: e.got
		    }) 
	}
}

impl ToSql for Blake2b512Digest {
	fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
	    Ok(ToSqlOutput::Borrowed(ValueRef::Blob(self.deref())))
	}
}