1use crate::{
3 AcquisitionPolicy, ContentIdentity, ContentTransferError, FlowRef, PeerAdvertisement, PeerId,
4};
5use std::collections::HashMap;
6use std::sync::{Arc, Mutex};
7
8#[derive(Clone)]
10pub struct ContentAddressableStore {
11 content: Arc<Mutex<HashMap<String, Vec<u8>>>>,
13 content_locations: Arc<Mutex<HashMap<String, Vec<PeerId>>>>,
15}
16
17impl ContentAddressableStore {
18 pub fn new() -> Self {
20 Self {
21 content: Arc::new(Mutex::new(HashMap::new())),
22 content_locations: Arc::new(Mutex::new(HashMap::new())),
23 }
24 }
25
26 pub fn store(&self, content: Vec<u8>, hash: String) -> Result<(), String> {
28 let mut store = self.content.lock().map_err(|e| e.to_string())?;
29 store.insert(hash, content);
30 Ok(())
31 }
32
33 pub fn get(&self, hash: &str) -> Result<Option<Vec<u8>>, String> {
35 let store = self.content.lock().map_err(|e| e.to_string())?;
36 Ok(store.get(hash).cloned())
37 }
38
39 pub fn record_location(&self, hash: String, peer: PeerId) -> Result<(), String> {
41 let mut locations = self.content_locations.lock().map_err(|e| e.to_string())?;
42 locations.entry(hash).or_insert_with(Vec::new).push(peer);
43 Ok(())
44 }
45
46 pub fn find_peers_for_content(&self, hash: &str) -> Result<Vec<PeerId>, String> {
48 let locations = self.content_locations.lock().map_err(|e| e.to_string())?;
49 Ok(locations.get(hash).cloned().unwrap_or_default())
50 }
51
52 pub fn has_content(&self, hash: &str) -> Result<bool, String> {
54 let store = self.content.lock().map_err(|e| e.to_string())?;
55 Ok(store.contains_key(hash))
56 }
57}
58
59impl Default for ContentAddressableStore {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65pub struct DistributedReferenceResolver {
67 _local_store: ContentAddressableStore,
69}
70
71impl DistributedReferenceResolver {
72 pub fn new(local_store: ContentAddressableStore) -> Self {
74 Self {
75 _local_store: local_store,
76 }
77 }
78
79 pub fn resolve(
81 &self,
82 flow_ref: &FlowRef,
83 available_peers: &[PeerAdvertisement],
84 ) -> Result<Option<Vec<u8>>, String> {
85 for peer_ad in available_peers {
92 if peer_ad.provides_namespace(&flow_ref.namespace) {
93 return Ok(None); }
97 }
98
99 Ok(None)
100 }
101}
102
103#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
105pub struct ProjectionDistribution {
106 pub namespace: String,
108 pub projection_type: String,
110 pub hosted_by: PeerId,
112 pub updated_at_ms: u128,
114}
115
116impl ProjectionDistribution {
117 pub fn new(
119 namespace: impl Into<String>,
120 projection_type: impl Into<String>,
121 hosted_by: PeerId,
122 ) -> Self {
123 Self {
124 namespace: namespace.into(),
125 projection_type: projection_type.into(),
126 hosted_by,
127 updated_at_ms: crate::now_ms(),
128 }
129 }
130}
131
132#[derive(Clone)]
134pub struct CapabilityLocationRegistry {
135 capability_locations: Arc<Mutex<HashMap<String, Vec<PeerId>>>>,
137 peer_capabilities: Arc<Mutex<HashMap<String, Vec<String>>>>,
139}
140
141impl CapabilityLocationRegistry {
142 pub fn new() -> Self {
144 Self {
145 capability_locations: Arc::new(Mutex::new(HashMap::new())),
146 peer_capabilities: Arc::new(Mutex::new(HashMap::new())),
147 }
148 }
149
150 pub fn register_capability(&self, capability: String, peer: PeerId) -> Result<(), String> {
152 let mut locations = self
153 .capability_locations
154 .lock()
155 .map_err(|e| e.to_string())?;
156 locations
157 .entry(capability.clone())
158 .or_insert_with(Vec::new)
159 .push(peer.clone());
160
161 let mut peer_caps = self.peer_capabilities.lock().map_err(|e| e.to_string())?;
162 peer_caps
163 .entry(peer.id.clone())
164 .or_insert_with(Vec::new)
165 .push(capability);
166
167 Ok(())
168 }
169
170 pub fn peers_for_capability(&self, capability: &str) -> Result<Vec<PeerId>, String> {
172 let locations = self
173 .capability_locations
174 .lock()
175 .map_err(|e| e.to_string())?;
176 Ok(locations.get(capability).cloned().unwrap_or_default())
177 }
178
179 pub fn capabilities_for_peer(&self, peer_id: &PeerId) -> Result<Vec<String>, String> {
181 let peer_caps = self.peer_capabilities.lock().map_err(|e| e.to_string())?;
182 Ok(peer_caps.get(&peer_id.id).cloned().unwrap_or_default())
183 }
184}
185
186impl Default for CapabilityLocationRegistry {
187 fn default() -> Self {
188 Self::new()
189 }
190}
191
192#[derive(Clone)]
194pub struct ContentAcquisitionManager {
195 content_store: ContentAddressableStore,
197 peer_registry: Arc<Mutex<HashMap<String, PeerAdvertisement>>>,
199}
200
201impl ContentAcquisitionManager {
202 pub fn new(content_store: ContentAddressableStore) -> Self {
204 Self {
205 content_store,
206 peer_registry: Arc::new(Mutex::new(HashMap::new())),
207 }
208 }
209
210 pub fn register_peer(&self, advertisement: PeerAdvertisement) -> Result<(), String> {
212 let mut registry = self.peer_registry.lock().map_err(|e| e.to_string())?;
213 registry.insert(advertisement.peer_id.id.clone(), advertisement);
214 Ok(())
215 }
216
217 pub fn resolve(
219 &self,
220 flow_ref: &FlowRef,
221 policy: AcquisitionPolicy,
222 ) -> Result<Option<Vec<u8>>, String> {
223 let local_key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
225
226 if let Ok(Some(content)) = self.content_store.get(&local_key) {
229 return Ok(Some(content));
230 }
231
232 match policy {
234 AcquisitionPolicy::LocalOnly => {
235 Ok(None)
237 }
238 AcquisitionPolicy::PreferLocal
239 | AcquisitionPolicy::RemoteAllowed
240 | AcquisitionPolicy::Required => {
241 self.find_and_acquire_from_peer(flow_ref)
244 }
245 }
246 }
247
248 fn find_and_acquire_from_peer(&self, flow_ref: &FlowRef) -> Result<Option<Vec<u8>>, String> {
250 let registry = self.peer_registry.lock().map_err(|e| e.to_string())?;
251
252 let providers: Vec<_> = registry
254 .values()
255 .filter(|ad| ad.provides_namespace(&flow_ref.namespace))
256 .collect();
257
258 if providers.is_empty() {
259 return Ok(None);
260 }
261
262 if providers.first().is_some() {
265 Ok(None)
268 } else {
269 Ok(None)
270 }
271 }
272
273 pub fn verify_content(
275 content: &[u8],
276 expected_identity: &ContentIdentity,
277 ) -> Result<(), ContentTransferError> {
278 let computed_identity = ContentIdentity::from_bytes(content);
279
280 if computed_identity.hash == expected_identity.hash {
281 Ok(())
282 } else {
283 Err(ContentTransferError::HashMismatch {
284 expected: expected_identity.hash.clone(),
285 computed: computed_identity.hash,
286 })
287 }
288 }
289
290 pub fn materialize(
292 &self,
293 flow_ref: &FlowRef,
294 content: Vec<u8>,
295 content_identity: &ContentIdentity,
296 ) -> Result<(), String> {
297 Self::verify_content(&content, content_identity).map_err(|e| e.to_string())?;
299
300 let key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
302 self.content_store.store(content, key)?;
303
304 Ok(())
305 }
306
307 pub fn get_local(&self, flow_ref: &FlowRef) -> Result<Option<Vec<u8>>, String> {
309 let key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
310 self.content_store.get(&key)
311 }
312
313 pub fn has_local(&self, flow_ref: &FlowRef) -> Result<bool, String> {
315 let key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
316 self.content_store.has_content(&key)
317 }
318}
319
320impl Default for ContentAcquisitionManager {
321 fn default() -> Self {
322 Self::new(ContentAddressableStore::new())
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn test_content_addressable_store() {
332 let store = ContentAddressableStore::new();
333 let content = b"hello world".to_vec();
334 let hash = "abc123".to_string();
335
336 store.store(content.clone(), hash.clone()).unwrap();
337 let retrieved = store.get(&hash).unwrap();
338 assert_eq!(retrieved, Some(content));
339 }
340
341 #[test]
342 fn test_content_addressable_store_locations() {
343 let store = ContentAddressableStore::new();
344 let hash = "abc123".to_string();
345 let peer_a = PeerId::new("peer-a");
346 let peer_b = PeerId::new("peer-b");
347
348 store.record_location(hash.clone(), peer_a).unwrap();
349 store.record_location(hash.clone(), peer_b).unwrap();
350
351 let peers = store.find_peers_for_content(&hash).unwrap();
352 assert_eq!(peers.len(), 2);
353 }
354
355 #[test]
356 fn test_projection_distribution() {
357 let peer = PeerId::new("search-node");
358 let proj = ProjectionDistribution::new("docs", "search", peer.clone());
359
360 assert_eq!(proj.namespace, "docs");
361 assert_eq!(proj.projection_type, "search");
362 assert_eq!(proj.hosted_by.id, "search-node");
363 }
364
365 #[test]
366 fn test_capability_location_registry() {
367 let registry = CapabilityLocationRegistry::new();
368 let peer_a = PeerId::new("peer-a");
369 let peer_b = PeerId::new("peer-b");
370
371 registry
372 .register_capability("search:docs".to_string(), peer_a.clone())
373 .unwrap();
374 registry
375 .register_capability("search:docs".to_string(), peer_b.clone())
376 .unwrap();
377 registry
378 .register_capability("vector:embeddings".to_string(), peer_b.clone())
379 .unwrap();
380
381 let search_peers = registry.peers_for_capability("search:docs").unwrap();
382 assert_eq!(search_peers.len(), 2);
383
384 let vector_peers = registry.peers_for_capability("vector:embeddings").unwrap();
385 assert_eq!(vector_peers.len(), 1);
386
387 let peer_a_caps = registry.capabilities_for_peer(&peer_a).unwrap();
388 assert_eq!(peer_a_caps.len(), 1);
389
390 let peer_b_caps = registry.capabilities_for_peer(&peer_b).unwrap();
391 assert_eq!(peer_b_caps.len(), 2);
392 }
393
394 #[test]
395 fn test_content_acquisition_manager_local_only() {
396 let store = ContentAddressableStore::new();
397 let manager = ContentAcquisitionManager::new(store);
398
399 let flow_ref = FlowRef::new("docs", "123");
400
401 let result = manager
403 .resolve(&flow_ref, AcquisitionPolicy::LocalOnly)
404 .unwrap();
405 assert!(result.is_none());
406 }
407
408 #[test]
409 fn test_content_acquisition_manager_materialize() {
410 let store = ContentAddressableStore::new();
411 let manager = ContentAcquisitionManager::new(store);
412
413 let flow_ref = FlowRef::new("docs", "123");
414 let content = b"test content".to_vec();
415 let content_id = ContentIdentity::from_bytes(&content);
416
417 manager
419 .materialize(&flow_ref, content.clone(), &content_id)
420 .unwrap();
421
422 let retrieved = manager.get_local(&flow_ref).unwrap();
424 assert_eq!(retrieved, Some(content));
425 }
426
427 #[test]
428 fn test_content_acquisition_manager_has_local() {
429 let store = ContentAddressableStore::new();
430 let manager = ContentAcquisitionManager::new(store);
431
432 let flow_ref = FlowRef::new("docs", "123");
433
434 assert!(!manager.has_local(&flow_ref).unwrap());
436
437 let content = b"test content".to_vec();
439 let content_id = ContentIdentity::from_bytes(&content);
440 manager
441 .materialize(&flow_ref, content, &content_id)
442 .unwrap();
443
444 assert!(manager.has_local(&flow_ref).unwrap());
446 }
447
448 #[test]
449 fn test_verify_content_success() {
450 let content = b"test content".to_vec();
451 let content_id = ContentIdentity::from_bytes(&content);
452
453 let result = ContentAcquisitionManager::verify_content(&content, &content_id);
454 assert!(result.is_ok());
455 }
456
457 #[test]
458 fn test_verify_content_hash_mismatch() {
459 let content = b"test content".to_vec();
460 let wrong_id = ContentIdentity::new("wronghash");
461
462 let result = ContentAcquisitionManager::verify_content(&content, &wrong_id);
463 assert!(result.is_err());
464 }
465}