use std::sync::Arc;
use async_trait::async_trait;
use bytes::Bytes;
use crate::BlakeHash;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum FetchTier {
Cache = 0,
Lan = 1,
Swarm = 2,
Seed = 3,
Cdn = 4,
}
impl FetchTier {
pub fn label(self) -> &'static str {
match self {
FetchTier::Cache => "cache",
FetchTier::Lan => "lan",
FetchTier::Swarm => "swarm",
FetchTier::Seed => "seed",
FetchTier::Cdn => "cdn",
}
}
}
#[async_trait]
pub(crate) trait BlobSource: Send + Sync {
async fn fetch_raw(&self, hash: &BlakeHash) -> Option<Bytes>;
fn tier(&self) -> FetchTier;
}
pub(crate) struct FetchChain {
sources: Vec<Arc<dyn BlobSource>>,
}
impl FetchChain {
pub fn new(mut sources: Vec<Arc<dyn BlobSource>>) -> Self {
sources.sort_by_key(|s| s.tier());
FetchChain { sources }
}
pub async fn fetch(&self, hash: &BlakeHash) -> Option<(FetchTier, Bytes)> {
for source in &self.sources {
let Some(bytes) = source.fetch_raw(hash).await else {
continue;
};
if hash.verify(&bytes) {
tracing::debug!(tier = source.tier().label(), hash = %hash, "fetch hit");
return Some((source.tier(), bytes));
}
tracing::warn!(
tier = source.tier().label(),
hash = %hash,
"BLAKE3 mismatch — dropping source"
);
}
None
}
}