use crate::common::hash::{self, Hasher}; use rusqlite::types::{FromSql, FromSqlError}; use rusqlite::{ types::{FromSqlResult, ToSqlOutput}, ToSql, }; use serde::{Deserialize, Serialize}; use std::fmt::Display; use std::path::PathBuf; use std::str::FromStr; #[derive(Debug)] pub enum Error { InvalidHash(hash::Error), } type Result = std::result::Result; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq)] pub struct BlobRef(String); impl BlobRef { pub fn from_str(s: &str) -> Result { hash::Hash::from_str(s).map_err(Error::InvalidHash)?; Ok(Self(s.to_string())) } pub fn for_bytes(bytes: &[u8]) -> Self { let mut hasher = Hasher::default(); hasher.update(bytes); BlobRef(hasher.finish().to_base32()) } pub(super) fn path(&self) -> PathBuf { let mut buf = PathBuf::new(); buf.push(&self.0[0..4]); buf.push(&self.0[4..]); buf } } impl Display for BlobRef { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "blobref:{}", self.0) } } impl ToSql for BlobRef { fn to_sql(&self) -> rusqlite::Result> { Ok(ToSqlOutput::Borrowed(rusqlite::types::ValueRef::Text( self.0.as_bytes(), ))) } } impl FromSql for BlobRef { fn column_result(value: rusqlite::types::ValueRef<'_>) -> FromSqlResult { let v: String = FromSql::column_result(value)?; BlobRef::from_str(&v).map_err(|_| FromSqlError::InvalidType) } }