use crate::{
AcquisitionPolicy, ContentIdentity, ContentTransferError, FlowRef, PeerAdvertisement, PeerId,
};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Clone)]
pub struct ContentAddressableStore {
content: Arc<Mutex<HashMap<String, Vec<u8>>>>,
content_locations: Arc<Mutex<HashMap<String, Vec<PeerId>>>>,
}
impl ContentAddressableStore {
pub fn new() -> Self {
Self {
content: Arc::new(Mutex::new(HashMap::new())),
content_locations: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn store(&self, content: Vec<u8>, hash: String) -> Result<(), String> {
let mut store = self.content.lock().map_err(|e| e.to_string())?;
store.insert(hash, content);
Ok(())
}
pub fn get(&self, hash: &str) -> Result<Option<Vec<u8>>, String> {
let store = self.content.lock().map_err(|e| e.to_string())?;
Ok(store.get(hash).cloned())
}
pub fn record_location(&self, hash: String, peer: PeerId) -> Result<(), String> {
let mut locations = self.content_locations.lock().map_err(|e| e.to_string())?;
locations.entry(hash).or_insert_with(Vec::new).push(peer);
Ok(())
}
pub fn find_peers_for_content(&self, hash: &str) -> Result<Vec<PeerId>, String> {
let locations = self.content_locations.lock().map_err(|e| e.to_string())?;
Ok(locations.get(hash).cloned().unwrap_or_default())
}
pub fn has_content(&self, hash: &str) -> Result<bool, String> {
let store = self.content.lock().map_err(|e| e.to_string())?;
Ok(store.contains_key(hash))
}
}
impl Default for ContentAddressableStore {
fn default() -> Self {
Self::new()
}
}
pub struct DistributedReferenceResolver {
_local_store: ContentAddressableStore,
}
impl DistributedReferenceResolver {
pub fn new(local_store: ContentAddressableStore) -> Self {
Self {
_local_store: local_store,
}
}
pub fn resolve(
&self,
flow_ref: &FlowRef,
available_peers: &[PeerAdvertisement],
) -> Result<Option<Vec<u8>>, String> {
for peer_ad in available_peers {
if peer_ad.provides_namespace(&flow_ref.namespace) {
return Ok(None); }
}
Ok(None)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ProjectionDistribution {
pub namespace: String,
pub projection_type: String,
pub hosted_by: PeerId,
pub updated_at_ms: u128,
}
impl ProjectionDistribution {
pub fn new(
namespace: impl Into<String>,
projection_type: impl Into<String>,
hosted_by: PeerId,
) -> Self {
Self {
namespace: namespace.into(),
projection_type: projection_type.into(),
hosted_by,
updated_at_ms: crate::now_ms(),
}
}
}
#[derive(Clone)]
pub struct CapabilityLocationRegistry {
capability_locations: Arc<Mutex<HashMap<String, Vec<PeerId>>>>,
peer_capabilities: Arc<Mutex<HashMap<String, Vec<String>>>>,
}
impl CapabilityLocationRegistry {
pub fn new() -> Self {
Self {
capability_locations: Arc::new(Mutex::new(HashMap::new())),
peer_capabilities: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn register_capability(&self, capability: String, peer: PeerId) -> Result<(), String> {
let mut locations = self
.capability_locations
.lock()
.map_err(|e| e.to_string())?;
locations
.entry(capability.clone())
.or_insert_with(Vec::new)
.push(peer.clone());
let mut peer_caps = self.peer_capabilities.lock().map_err(|e| e.to_string())?;
peer_caps
.entry(peer.id.clone())
.or_insert_with(Vec::new)
.push(capability);
Ok(())
}
pub fn peers_for_capability(&self, capability: &str) -> Result<Vec<PeerId>, String> {
let locations = self
.capability_locations
.lock()
.map_err(|e| e.to_string())?;
Ok(locations.get(capability).cloned().unwrap_or_default())
}
pub fn capabilities_for_peer(&self, peer_id: &PeerId) -> Result<Vec<String>, String> {
let peer_caps = self.peer_capabilities.lock().map_err(|e| e.to_string())?;
Ok(peer_caps.get(&peer_id.id).cloned().unwrap_or_default())
}
}
impl Default for CapabilityLocationRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone)]
pub struct ContentAcquisitionManager {
content_store: ContentAddressableStore,
peer_registry: Arc<Mutex<HashMap<String, PeerAdvertisement>>>,
}
impl ContentAcquisitionManager {
pub fn new(content_store: ContentAddressableStore) -> Self {
Self {
content_store,
peer_registry: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn register_peer(&self, advertisement: PeerAdvertisement) -> Result<(), String> {
let mut registry = self.peer_registry.lock().map_err(|e| e.to_string())?;
registry.insert(advertisement.peer_id.id.clone(), advertisement);
Ok(())
}
pub fn resolve(
&self,
flow_ref: &FlowRef,
policy: AcquisitionPolicy,
) -> Result<Option<Vec<u8>>, String> {
let local_key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
if let Ok(Some(content)) = self.content_store.get(&local_key) {
return Ok(Some(content));
}
match policy {
AcquisitionPolicy::LocalOnly => {
Ok(None)
}
AcquisitionPolicy::PreferLocal
| AcquisitionPolicy::RemoteAllowed
| AcquisitionPolicy::Required => {
self.find_and_acquire_from_peer(flow_ref)
}
}
}
fn find_and_acquire_from_peer(&self, flow_ref: &FlowRef) -> Result<Option<Vec<u8>>, String> {
let registry = self.peer_registry.lock().map_err(|e| e.to_string())?;
let providers: Vec<_> = registry
.values()
.filter(|ad| ad.provides_namespace(&flow_ref.namespace))
.collect();
if providers.is_empty() {
return Ok(None);
}
if providers.first().is_some() {
Ok(None)
} else {
Ok(None)
}
}
pub fn verify_content(
content: &[u8],
expected_identity: &ContentIdentity,
) -> Result<(), ContentTransferError> {
let computed_identity = ContentIdentity::from_bytes(content);
if computed_identity.hash == expected_identity.hash {
Ok(())
} else {
Err(ContentTransferError::HashMismatch {
expected: expected_identity.hash.clone(),
computed: computed_identity.hash,
})
}
}
pub fn materialize(
&self,
flow_ref: &FlowRef,
content: Vec<u8>,
content_identity: &ContentIdentity,
) -> Result<(), String> {
Self::verify_content(&content, content_identity).map_err(|e| e.to_string())?;
let key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
self.content_store.store(content, key)?;
Ok(())
}
pub fn get_local(&self, flow_ref: &FlowRef) -> Result<Option<Vec<u8>>, String> {
let key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
self.content_store.get(&key)
}
pub fn has_local(&self, flow_ref: &FlowRef) -> Result<bool, String> {
let key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
self.content_store.has_content(&key)
}
}
impl Default for ContentAcquisitionManager {
fn default() -> Self {
Self::new(ContentAddressableStore::new())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_content_addressable_store() {
let store = ContentAddressableStore::new();
let content = b"hello world".to_vec();
let hash = "abc123".to_string();
store.store(content.clone(), hash.clone()).unwrap();
let retrieved = store.get(&hash).unwrap();
assert_eq!(retrieved, Some(content));
}
#[test]
fn test_content_addressable_store_locations() {
let store = ContentAddressableStore::new();
let hash = "abc123".to_string();
let peer_a = PeerId::new("peer-a");
let peer_b = PeerId::new("peer-b");
store.record_location(hash.clone(), peer_a).unwrap();
store.record_location(hash.clone(), peer_b).unwrap();
let peers = store.find_peers_for_content(&hash).unwrap();
assert_eq!(peers.len(), 2);
}
#[test]
fn test_projection_distribution() {
let peer = PeerId::new("search-node");
let proj = ProjectionDistribution::new("docs", "search", peer.clone());
assert_eq!(proj.namespace, "docs");
assert_eq!(proj.projection_type, "search");
assert_eq!(proj.hosted_by.id, "search-node");
}
#[test]
fn test_capability_location_registry() {
let registry = CapabilityLocationRegistry::new();
let peer_a = PeerId::new("peer-a");
let peer_b = PeerId::new("peer-b");
registry
.register_capability("search:docs".to_string(), peer_a.clone())
.unwrap();
registry
.register_capability("search:docs".to_string(), peer_b.clone())
.unwrap();
registry
.register_capability("vector:embeddings".to_string(), peer_b.clone())
.unwrap();
let search_peers = registry.peers_for_capability("search:docs").unwrap();
assert_eq!(search_peers.len(), 2);
let vector_peers = registry.peers_for_capability("vector:embeddings").unwrap();
assert_eq!(vector_peers.len(), 1);
let peer_a_caps = registry.capabilities_for_peer(&peer_a).unwrap();
assert_eq!(peer_a_caps.len(), 1);
let peer_b_caps = registry.capabilities_for_peer(&peer_b).unwrap();
assert_eq!(peer_b_caps.len(), 2);
}
#[test]
fn test_content_acquisition_manager_local_only() {
let store = ContentAddressableStore::new();
let manager = ContentAcquisitionManager::new(store);
let flow_ref = FlowRef::new("docs", "123");
let result = manager
.resolve(&flow_ref, AcquisitionPolicy::LocalOnly)
.unwrap();
assert!(result.is_none());
}
#[test]
fn test_content_acquisition_manager_materialize() {
let store = ContentAddressableStore::new();
let manager = ContentAcquisitionManager::new(store);
let flow_ref = FlowRef::new("docs", "123");
let content = b"test content".to_vec();
let content_id = ContentIdentity::from_bytes(&content);
manager
.materialize(&flow_ref, content.clone(), &content_id)
.unwrap();
let retrieved = manager.get_local(&flow_ref).unwrap();
assert_eq!(retrieved, Some(content));
}
#[test]
fn test_content_acquisition_manager_has_local() {
let store = ContentAddressableStore::new();
let manager = ContentAcquisitionManager::new(store);
let flow_ref = FlowRef::new("docs", "123");
assert!(!manager.has_local(&flow_ref).unwrap());
let content = b"test content".to_vec();
let content_id = ContentIdentity::from_bytes(&content);
manager
.materialize(&flow_ref, content, &content_id)
.unwrap();
assert!(manager.has_local(&flow_ref).unwrap());
}
#[test]
fn test_verify_content_success() {
let content = b"test content".to_vec();
let content_id = ContentIdentity::from_bytes(&content);
let result = ContentAcquisitionManager::verify_content(&content, &content_id);
assert!(result.is_ok());
}
#[test]
fn test_verify_content_hash_mismatch() {
let content = b"test content".to_vec();
let wrong_id = ContentIdentity::new("wronghash");
let result = ContentAcquisitionManager::verify_content(&content, &wrong_id);
assert!(result.is_err());
}
}