git-remote-htree 0.2.82

Git remote helper for hashtree - push/pull git repos via nostr and hashtree
Documentation
use hashtree_blossom::BlossomStore;
use hashtree_core::{Hash, Store, StoreError};
use sha2::{Digest, Sha256};
use std::sync::Arc;
use tracing::debug;

pub(super) struct CachedStore {
    local: Arc<dyn Store + Send + Sync>,
    blossom: BlossomStore,
}

impl CachedStore {
    pub(super) fn new(local: Arc<dyn Store + Send + Sync>, blossom: BlossomStore) -> Self {
        Self { local, blossom }
    }
}

#[async_trait::async_trait]
impl Store for CachedStore {
    async fn put(&self, hash: Hash, data: Vec<u8>) -> Result<bool, StoreError> {
        self.local.put(hash, data).await
    }

    async fn get(&self, hash: &Hash) -> Result<Option<Vec<u8>>, StoreError> {
        if let Ok(Some(data)) = self.local.get(hash).await {
            let computed = Sha256::digest(&data);
            if computed[..] == hash[..] {
                return Ok(Some(data));
            }
            debug!(
                "Ignoring corrupt local cache entry for {}",
                hex::encode(hash)
            );
        }

        let mut last_result = Ok(None);
        for attempt in 0..4 {
            let result = self.blossom.get(hash).await;
            match result {
                Ok(Some(data)) => {
                    let _ = self.local.put(*hash, data.clone()).await;
                    return Ok(Some(data));
                }
                Ok(None) if attempt < 3 => {
                    tokio::time::sleep(std::time::Duration::from_millis(
                        100 * (attempt + 1) as u64,
                    ))
                    .await;
                    last_result = Ok(None);
                }
                Ok(None) => return Ok(None),
                Err(err) if attempt < 3 => {
                    last_result = Err(err);
                    tokio::time::sleep(std::time::Duration::from_millis(
                        100 * (attempt + 1) as u64,
                    ))
                    .await;
                }
                Err(err) => return Err(err),
            }
        }

        last_result
    }

    async fn has(&self, hash: &Hash) -> Result<bool, StoreError> {
        if self.local.has(hash).await? {
            return Ok(true);
        }
        self.blossom.has(hash).await
    }

    async fn delete(&self, hash: &Hash) -> Result<bool, StoreError> {
        self.local.delete(hash).await
    }
}