use std::fmt::Display;
use ps_datachunk::{utils::decrypt, DataChunk};
use ps_hash::Hash;
use ps_promise::PromiseRejection;
use ps_util::ToResult;
use crate::{AsyncStore, Hkey, HkeyError, Store};
use super::LongHkeyExpanded;
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct LongHkey {
hash: Hash,
key: Hash,
}
impl Display for LongHkey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("L{}{}", self.hash, self.key))
}
}
impl LongHkey {
#[must_use]
pub const fn from_hash_and_key(hash: Hash, key: Hash) -> Self {
Self { hash, key }
}
#[must_use]
pub const fn hash(&self) -> Hash {
self.hash
}
#[must_use]
pub const fn hash_ref(&self) -> &Hash {
&self.hash
}
#[must_use]
pub const fn key(&self) -> Hash {
self.key
}
#[must_use]
pub const fn key_ref(&self) -> &Hash {
&self.key
}
pub fn expand_from_lhkey_str(expanded_data: &[u8]) -> Result<LongHkeyExpanded, HkeyError> {
if expanded_data.len() < 6 {
Err(HkeyError::Format)?;
}
if expanded_data[0] != b'{' || expanded_data[expanded_data.len() - 1] != b'}' {
Err(HkeyError::Format)?;
}
let parts_data = &expanded_data[1..expanded_data.len() - 1];
let parts_data = std::str::from_utf8(parts_data);
let parts_data = parts_data.map_err(HkeyError::from)?;
let parts: Vec<&str> = parts_data.split(';').collect();
if parts.len() != 3 {
Err(HkeyError::Format)?;
}
let depth: u32 = parts[0].parse().map_err(HkeyError::from)?;
let size: usize = parts[1].parse().map_err(HkeyError::from)?;
let parts = parts[2].split(',').map(|part| {
let (range, hkey) = part.split_once(':').ok_or(HkeyError::Format)?;
let (start, end) = range.split_once('-').ok_or(HkeyError::Format)?;
let start: usize = start.parse()?;
let end: usize = end.parse()?;
let hkey: Hkey = Hkey::parse(hkey).map_err(HkeyError::Construction)?;
#[allow(clippy::range_plus_one)]
Ok((start..end + 1, hkey))
});
let parts: Result<Vec<_>, HkeyError> = parts.collect();
let parts = parts?.into_boxed_slice().into();
LongHkeyExpanded::new(depth, size, parts).ok()
}
#[inline]
pub fn expand_from_lhkey_encrypted_str(
&self,
encrypted: &[u8],
) -> Result<LongHkeyExpanded, HkeyError> {
let lhkey_str = decrypt(encrypted, &self.key)?;
Self::expand_from_lhkey_str(lhkey_str.data_ref())
}
#[inline]
pub fn expand<'a, C, E, S>(&self, store: &'a S) -> Result<LongHkeyExpanded, E>
where
C: DataChunk,
E: From<HkeyError> + Send,
S: Store<Chunk<'a> = C, Error = E> + Sync + 'a,
{
let encrypted = store.get(&self.hash)?;
Self::expand_from_lhkey_encrypted_str(self, encrypted.data_ref())?.ok()
}
#[inline]
pub async fn expand_async<C, E, S>(&self, resolver: &S) -> Result<LongHkeyExpanded, E>
where
C: DataChunk + Send + Unpin,
E: From<HkeyError> + PromiseRejection + Send,
S: AsyncStore<Chunk = C, Error = E> + Sync,
{
let future = resolver.get(&self.hash);
let chunk = future.await?;
let bytes = chunk.data_ref();
Self::expand_from_lhkey_encrypted_str(self, bytes)?.ok()
}
}