ps-hkey 0.1.0-34

This crate defines the hashkey format and provides methods for resolving them
Documentation
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 {
            // empty array: {0;0;}
            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()
    }
}