1use crate::Operation;
2use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use std::collections::HashMap;
13use std::fmt;
14
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
17pub struct CapabilityId(pub String);
18
19impl fmt::Display for CapabilityId {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 write!(f, "{}", self.0)
22 }
23}
24
25impl CapabilityId {
26 pub fn new(id: String) -> Self {
27 CapabilityId(id)
28 }
29}
30
31#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
33pub enum CapabilityRole {
34 ExecutionEngine,
36 Projection,
38 ExecutionWithProjection,
40}
41
42#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
44pub enum CapabilityLifecycleState {
45 Registered,
47 Initializing,
49 CatchingUp,
51 Active,
53 Paused,
55 Rebuilding,
57 Removed,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct CapabilityContext {
64 pub capability_id: CapabilityId,
66 pub instance_id: String,
68 pub sequence: u64,
70 pub version: u32,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct CapabilityQuery {
77 pub query_type: String,
79 pub params: Value,
81}
82
83impl CapabilityQuery {
84 pub fn new(query_type: String, params: Value) -> Self {
85 Self { query_type, params }
86 }
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct CapabilityCheckpoint {
92 pub capability_id: CapabilityId,
94 pub last_processed_instance_id: String,
96 pub last_processed_sequence: u64,
98 pub lifecycle_state: CapabilityLifecycleState,
100 pub checkpoint_ms: u128,
102 pub version: u32,
104}
105
106impl CapabilityCheckpoint {
107 pub fn new(capability_id: CapabilityId, instance_id: String) -> Self {
108 Self {
109 capability_id,
110 last_processed_instance_id: instance_id,
111 last_processed_sequence: 0,
112 lifecycle_state: CapabilityLifecycleState::Registered,
113 checkpoint_ms: crate::now_ms(),
114 version: 1,
115 }
116 }
117
118 pub fn update_position(&mut self, instance_id: String, sequence: u64) {
120 self.last_processed_instance_id = instance_id;
121 self.last_processed_sequence = sequence;
122 self.checkpoint_ms = crate::now_ms();
123 }
124
125 pub fn set_state(&mut self, state: CapabilityLifecycleState) {
127 self.lifecycle_state = state;
128 self.checkpoint_ms = crate::now_ms();
129 }
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct CapabilityOperationResult {
135 pub success: bool,
137 pub error: Option<String>,
139 pub result: Option<Value>,
141 pub produced_derived_state: bool,
143}
144
145impl CapabilityOperationResult {
146 pub fn success() -> Self {
147 Self {
148 success: true,
149 error: None,
150 result: None,
151 produced_derived_state: false,
152 }
153 }
154
155 pub fn with_derived_state(result: Value) -> Self {
156 Self {
157 success: true,
158 error: None,
159 result: Some(result),
160 produced_derived_state: true,
161 }
162 }
163
164 pub fn error(msg: String) -> Self {
165 Self {
166 success: false,
167 error: Some(msg),
168 result: None,
169 produced_derived_state: false,
170 }
171 }
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct CapabilityQueryResult {
177 pub success: bool,
179 pub results: Vec<Value>,
181 pub error: Option<String>,
183}
184
185impl CapabilityQueryResult {
186 pub fn success(results: Vec<Value>) -> Self {
187 Self {
188 success: true,
189 results,
190 error: None,
191 }
192 }
193
194 pub fn empty() -> Self {
195 Self {
196 success: true,
197 results: Vec::new(),
198 error: None,
199 }
200 }
201
202 pub fn error(msg: String) -> Self {
203 Self {
204 success: false,
205 results: Vec::new(),
206 error: Some(msg),
207 }
208 }
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct CapabilityAccessModel {
214 pub reads: Vec<String>,
216 pub writes: Vec<String>,
218 pub executes: Vec<String>,
220}
221
222impl CapabilityAccessModel {
223 pub fn new(reads: Vec<String>, writes: Vec<String>, executes: Vec<String>) -> Self {
224 Self {
225 reads,
226 writes,
227 executes,
228 }
229 }
230
231 pub fn can_read(&self, collection: &str) -> bool {
233 self.reads.contains(&collection.to_string()) || self.reads.iter().any(|r| r == "*")
234 }
235
236 pub fn can_write(&self, collection: &str) -> bool {
238 self.writes.contains(&collection.to_string()) || self.writes.iter().any(|w| w == "*")
239 }
240
241 pub fn can_execute(&self, system: &str) -> bool {
243 self.executes.contains(&system.to_string()) || self.executes.iter().any(|e| e == "*")
244 }
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct CapabilityMetadata {
250 pub id: CapabilityId,
251 pub name: String,
252 pub description: String,
253 pub role: CapabilityRole,
254 pub access_model: CapabilityAccessModel,
255 pub version: u32,
256 pub created_ms: u128,
257}
258
259impl CapabilityMetadata {
260 pub fn new(
261 id: CapabilityId,
262 name: String,
263 description: String,
264 role: CapabilityRole,
265 access_model: CapabilityAccessModel,
266 ) -> Self {
267 Self {
268 id,
269 name,
270 description,
271 role,
272 access_model,
273 version: 1,
274 created_ms: crate::now_ms(),
275 }
276 }
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct CapabilityRegistry {
282 #[serde(default)]
284 capabilities: HashMap<CapabilityId, CapabilityMetadata>,
285 #[serde(default)]
287 checkpoints: HashMap<CapabilityId, CapabilityCheckpoint>,
288}
289
290impl CapabilityRegistry {
291 pub fn new() -> Self {
292 Self {
293 capabilities: HashMap::new(),
294 checkpoints: HashMap::new(),
295 }
296 }
297
298 pub fn register(&mut self, metadata: CapabilityMetadata) -> crate::Result<()> {
300 let id = metadata.id.clone();
301 if self.capabilities.contains_key(&id) {
302 return Err(crate::FlowError::CapabilityError(
303 "Capability already registered".to_string(),
304 ));
305 }
306 let checkpoint = CapabilityCheckpoint::new(id.clone(), String::new());
307 self.capabilities.insert(id.clone(), metadata);
308 self.checkpoints.insert(id, checkpoint);
309 Ok(())
310 }
311
312 pub fn get(&self, id: &CapabilityId) -> Option<&CapabilityMetadata> {
314 self.capabilities.get(id)
315 }
316
317 pub fn get_checkpoint(&self, id: &CapabilityId) -> Option<&CapabilityCheckpoint> {
319 self.checkpoints.get(id)
320 }
321
322 pub fn update_checkpoint(&mut self, checkpoint: CapabilityCheckpoint) {
324 self.checkpoints
325 .insert(checkpoint.capability_id.clone(), checkpoint);
326 }
327
328 pub fn list(&self) -> Vec<&CapabilityMetadata> {
330 self.capabilities.values().collect()
331 }
332
333 pub fn remove(&mut self, id: &CapabilityId) -> Option<CapabilityMetadata> {
335 self.checkpoints.remove(id);
336 self.capabilities.remove(id)
337 }
338}
339
340impl Default for CapabilityRegistry {
341 fn default() -> Self {
342 Self::new()
343 }
344}
345
346pub trait FlowCapability: Send + Sync {
349 fn id(&self) -> CapabilityId;
351
352 fn metadata(&self) -> CapabilityMetadata;
354
355 fn initialize(&mut self, context: &CapabilityContext) -> crate::Result<()>;
357
358 fn on_operation(
360 &mut self,
361 operation: &Operation,
362 context: &CapabilityContext,
363 ) -> crate::Result<CapabilityOperationResult>;
364
365 fn query(
367 &self,
368 query: &CapabilityQuery,
369 context: &CapabilityContext,
370 ) -> crate::Result<CapabilityQueryResult>;
371
372 fn rebuild(
374 &mut self,
375 operations: &[Operation],
376 context: &CapabilityContext,
377 ) -> crate::Result<()>;
378
379 fn pause(&mut self) -> crate::Result<()> {
381 Ok(())
382 }
383
384 fn resume(&mut self) -> crate::Result<()> {
386 Ok(())
387 }
388
389 fn is_ready(&self) -> bool {
391 true
392 }
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
398pub struct CapabilityRef {
399 pub namespace: String,
401 pub capability: String,
403 pub version: u32,
405}
406
407impl CapabilityRef {
408 pub fn new(namespace: String, capability: String, version: u32) -> Self {
410 Self {
411 namespace,
412 capability,
413 version,
414 }
415 }
416
417 pub fn canonical(&self) -> String {
419 format!(
420 "flow://capability/{}/{}@{}",
421 self.namespace, self.capability, self.version
422 )
423 }
424
425 pub fn from_canonical(canonical: &str) -> crate::Result<Self> {
427 if !canonical.starts_with("flow://capability/") {
428 return Err(crate::FlowError::CapabilityError(
429 "Invalid capability reference format".to_string(),
430 ));
431 }
432
433 let rest = &canonical[18..]; let parts: Vec<&str> = rest.split('/').collect();
435 if parts.len() != 2 {
436 return Err(crate::FlowError::CapabilityError(
437 "Invalid capability reference format".to_string(),
438 ));
439 }
440
441 let namespace = parts[0].to_string();
442 let cap_parts: Vec<&str> = parts[1].split('@').collect();
443 if cap_parts.len() != 2 {
444 return Err(crate::FlowError::CapabilityError(
445 "Invalid capability reference format".to_string(),
446 ));
447 }
448
449 let capability = cap_parts[0].to_string();
450 let version = cap_parts[1]
451 .parse::<u32>()
452 .map_err(|_| crate::FlowError::CapabilityError("Invalid version number".to_string()))?;
453
454 Ok(Self::new(namespace, capability, version))
455 }
456}
457
458impl fmt::Display for CapabilityRef {
459 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460 write!(f, "{}", self.canonical())
461 }
462}
463
464#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
466pub struct StateRequirement {
467 pub namespace: String,
469 pub min_version: u64,
471}
472
473impl StateRequirement {
474 pub fn new(namespace: String, min_version: u64) -> Self {
475 Self {
476 namespace,
477 min_version,
478 }
479 }
480}
481
482#[derive(Debug, Clone, Serialize, Deserialize)]
484pub struct CapabilityRequirements {
485 pub required_state: Vec<StateRequirement>,
487 pub max_state_latency_ms: Option<u64>,
489 pub requires_ownership: bool,
491}
492
493impl CapabilityRequirements {
494 pub fn new() -> Self {
495 Self {
496 required_state: Vec::new(),
497 max_state_latency_ms: None,
498 requires_ownership: false,
499 }
500 }
501
502 pub fn with_required_state(mut self, requirement: StateRequirement) -> Self {
503 self.required_state.push(requirement);
504 self
505 }
506
507 pub fn with_max_latency(mut self, latency_ms: u64) -> Self {
508 self.max_state_latency_ms = Some(latency_ms);
509 self
510 }
511
512 pub fn with_ownership_requirement(mut self, requires: bool) -> Self {
513 self.requires_ownership = requires;
514 self
515 }
516}
517
518impl Default for CapabilityRequirements {
519 fn default() -> Self {
520 Self::new()
521 }
522}
523
524#[derive(Debug, Clone, Serialize, Deserialize)]
526pub struct DistributedCapabilityAdvertisement {
527 pub capability_ref: CapabilityRef,
529 pub peer_id: String,
531 pub requirements: CapabilityRequirements,
533 pub advertised_ms: u128,
535}
536
537impl DistributedCapabilityAdvertisement {
538 pub fn new(capability_ref: CapabilityRef, peer_id: String) -> Self {
539 Self {
540 capability_ref,
541 peer_id,
542 requirements: CapabilityRequirements::new(),
543 advertised_ms: crate::now_ms(),
544 }
545 }
546
547 pub fn with_requirements(mut self, requirements: CapabilityRequirements) -> Self {
548 self.requirements = requirements;
549 self
550 }
551}
552
553#[derive(Debug, Clone, Serialize, Deserialize)]
555pub struct CapabilityExecutionContext {
556 pub execution_id: String,
558 pub capability_ref: CapabilityRef,
560 pub input_refs: Vec<String>,
562 pub required_state_version: u64,
564 pub requester: String,
566 pub requested_ms: u128,
568}
569
570impl CapabilityExecutionContext {
571 pub fn new(execution_id: String, capability_ref: CapabilityRef, requester: String) -> Self {
572 Self {
573 execution_id,
574 capability_ref,
575 input_refs: Vec::new(),
576 required_state_version: 0,
577 requester,
578 requested_ms: crate::now_ms(),
579 }
580 }
581
582 pub fn with_input_ref(mut self, input_ref: String) -> Self {
583 self.input_refs.push(input_ref);
584 self
585 }
586
587 pub fn with_required_state_version(mut self, version: u64) -> Self {
588 self.required_state_version = version;
589 self
590 }
591}
592
593#[derive(Debug, Clone, Serialize, Deserialize)]
595pub struct ExecutionRef {
596 pub execution_id: String,
598 pub capability_ref: CapabilityRef,
600 pub provider_peer: String,
602 pub input_refs: Vec<String>,
604 pub input_state_version: u64,
606 pub result_content_identity: String,
608 pub created_operation: Option<String>,
610}
611
612impl ExecutionRef {
613 pub fn new(execution_id: String, capability_ref: CapabilityRef, provider_peer: String) -> Self {
614 Self {
615 execution_id,
616 capability_ref,
617 provider_peer,
618 input_refs: Vec::new(),
619 input_state_version: 0,
620 result_content_identity: String::new(),
621 created_operation: None,
622 }
623 }
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize)]
628pub struct CapabilityExecutionResult {
629 pub execution_ref: ExecutionRef,
631 pub result: Value,
633 pub completed_ms: u128,
635 pub error: Option<String>,
637}
638
639impl CapabilityExecutionResult {
640 pub fn success(execution_ref: ExecutionRef, result: Value) -> Self {
641 Self {
642 execution_ref,
643 result,
644 completed_ms: crate::now_ms(),
645 error: None,
646 }
647 }
648
649 pub fn error(execution_ref: ExecutionRef, error: String) -> Self {
650 Self {
651 execution_ref,
652 result: Value::Null,
653 completed_ms: crate::now_ms(),
654 error: Some(error),
655 }
656 }
657}
658
659pub struct CapabilityResolver {
661 capabilities: HashMap<String, DistributedCapabilityAdvertisement>,
663}
664
665impl CapabilityResolver {
666 pub fn new() -> Self {
667 Self {
668 capabilities: HashMap::new(),
669 }
670 }
671
672 pub fn register(&mut self, ad: DistributedCapabilityAdvertisement) {
674 self.capabilities.insert(ad.capability_ref.canonical(), ad);
675 }
676
677 pub fn resolve(&self, cap_ref: &CapabilityRef) -> Option<&DistributedCapabilityAdvertisement> {
679 self.capabilities.get(&cap_ref.canonical())
680 }
681
682 pub fn resolve_all(
684 &self,
685 namespace: &str,
686 capability: &str,
687 ) -> Vec<&DistributedCapabilityAdvertisement> {
688 self.capabilities
689 .values()
690 .filter(|ad| {
691 ad.capability_ref.namespace == namespace
692 && ad.capability_ref.capability == capability
693 })
694 .collect()
695 }
696}
697
698impl Default for CapabilityResolver {
699 fn default() -> Self {
700 Self::new()
701 }
702}
703
704#[cfg(test)]
705mod tests {
706 use super::*;
707
708 #[test]
709 fn test_capability_id_creation() {
710 let id = CapabilityId::new("search".to_string());
711 assert_eq!(id.0, "search");
712 }
713
714 #[test]
715 fn test_capability_checkpoint() {
716 let mut checkpoint = CapabilityCheckpoint::new(
717 CapabilityId::new("search".to_string()),
718 "instance-1".to_string(),
719 );
720 assert_eq!(checkpoint.last_processed_sequence, 0);
721
722 checkpoint.update_position("instance-1".to_string(), 100);
723 assert_eq!(checkpoint.last_processed_sequence, 100);
724 }
725
726 #[test]
727 fn test_capability_access_model() {
728 let access = CapabilityAccessModel::new(
729 vec!["docs".to_string()],
730 vec!["docs.search".to_string()],
731 vec!["embedding-api".to_string()],
732 );
733
734 assert!(access.can_read("docs"));
735 assert!(!access.can_read("users"));
736 assert!(access.can_write("docs.search"));
737 assert!(access.can_execute("embedding-api"));
738 }
739
740 #[test]
741 fn test_capability_access_wildcard() {
742 let access =
743 CapabilityAccessModel::new(vec!["*".to_string()], vec!["*".to_string()], vec![]);
744
745 assert!(access.can_read("anything"));
746 assert!(access.can_write("anything"));
747 }
748
749 #[test]
750 fn test_capability_registry() {
751 let mut registry = CapabilityRegistry::new();
752 let metadata = CapabilityMetadata::new(
753 CapabilityId::new("search".to_string()),
754 "Search Capability".to_string(),
755 "Full-text search".to_string(),
756 CapabilityRole::Projection,
757 CapabilityAccessModel::new(
758 vec!["docs".to_string()],
759 vec!["docs.search".to_string()],
760 vec![],
761 ),
762 );
763
764 registry.register(metadata).unwrap();
765 let cap_id = CapabilityId::new("search".to_string());
766 assert!(registry.get(&cap_id).is_some());
767 assert!(registry.get_checkpoint(&cap_id).is_some());
768 }
769
770 #[test]
771 fn test_capability_ref_creation_and_canonical() {
772 let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
773 assert_eq!(cap_ref.namespace, "docs");
774 assert_eq!(cap_ref.capability, "vector-search");
775 assert_eq!(cap_ref.version, 1);
776 assert_eq!(
777 cap_ref.canonical(),
778 "flow://capability/docs/vector-search@1"
779 );
780 }
781
782 #[test]
783 fn test_capability_ref_parse_canonical() {
784 let canonical = "flow://capability/docs/vector-search@1";
785 let cap_ref = CapabilityRef::from_canonical(canonical).unwrap();
786 assert_eq!(cap_ref.namespace, "docs");
787 assert_eq!(cap_ref.capability, "vector-search");
788 assert_eq!(cap_ref.version, 1);
789 }
790
791 #[test]
792 fn test_capability_ref_display() {
793 let cap_ref = CapabilityRef::new("images".to_string(), "image-analysis".to_string(), 2);
794 assert_eq!(
795 cap_ref.to_string(),
796 "flow://capability/images/image-analysis@2"
797 );
798 }
799
800 #[test]
801 fn test_state_requirement_creation() {
802 let req = StateRequirement::new("docs".to_string(), 847);
803 assert_eq!(req.namespace, "docs");
804 assert_eq!(req.min_version, 847);
805 }
806
807 #[test]
808 fn test_capability_requirements_builder() {
809 let reqs = CapabilityRequirements::new()
810 .with_required_state(StateRequirement::new("docs".to_string(), 847))
811 .with_max_latency(5000)
812 .with_ownership_requirement(true);
813
814 assert_eq!(reqs.required_state.len(), 1);
815 assert_eq!(reqs.max_state_latency_ms, Some(5000));
816 assert!(reqs.requires_ownership);
817 }
818
819 #[test]
820 fn test_distributed_capability_advertisement() {
821 let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
822 let ad = DistributedCapabilityAdvertisement::new(cap_ref.clone(), "peer-b".to_string())
823 .with_requirements(
824 CapabilityRequirements::new()
825 .with_required_state(StateRequirement::new("docs".to_string(), 847)),
826 );
827
828 assert_eq!(ad.capability_ref, cap_ref);
829 assert_eq!(ad.peer_id, "peer-b");
830 assert_eq!(ad.requirements.required_state.len(), 1);
831 }
832
833 #[test]
834 fn test_capability_execution_context() {
835 let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
836 let ctx = CapabilityExecutionContext::new(
837 "exec-123".to_string(),
838 cap_ref.clone(),
839 "requester".to_string(),
840 )
841 .with_input_ref("flow://docs#123".to_string())
842 .with_required_state_version(851);
843
844 assert_eq!(ctx.execution_id, "exec-123");
845 assert_eq!(ctx.capability_ref, cap_ref);
846 assert_eq!(ctx.input_refs.len(), 1);
847 assert_eq!(ctx.required_state_version, 851);
848 }
849
850 #[test]
851 fn test_execution_ref_creation() {
852 let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
853 let exec_ref = ExecutionRef::new(
854 "exec-123".to_string(),
855 cap_ref.clone(),
856 "peer-b".to_string(),
857 );
858
859 assert_eq!(exec_ref.execution_id, "exec-123");
860 assert_eq!(exec_ref.capability_ref, cap_ref);
861 assert_eq!(exec_ref.provider_peer, "peer-b");
862 }
863
864 #[test]
865 fn test_capability_execution_result_success() {
866 let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
867 let exec_ref = ExecutionRef::new("exec-123".to_string(), cap_ref, "peer-b".to_string());
868 let result = serde_json::json!({"results": [1, 2, 3]});
869
870 let exec_result = CapabilityExecutionResult::success(exec_ref.clone(), result.clone());
871 assert!(exec_result.error.is_none());
872 assert_eq!(exec_result.result, result);
873 }
874
875 #[test]
876 fn test_capability_resolver_registration() {
877 let mut resolver = CapabilityResolver::new();
878 let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
879 let ad = DistributedCapabilityAdvertisement::new(cap_ref.clone(), "peer-b".to_string());
880
881 resolver.register(ad);
882
883 let resolved = resolver.resolve(&cap_ref);
884 assert!(resolved.is_some());
885 assert_eq!(resolved.unwrap().peer_id, "peer-b");
886 }
887
888 #[test]
889 fn test_capability_resolver_resolve_all() {
890 let mut resolver = CapabilityResolver::new();
891
892 let cap_ref_v1 = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
893 let ad1 = DistributedCapabilityAdvertisement::new(cap_ref_v1, "peer-a".to_string());
894 resolver.register(ad1);
895
896 let cap_ref_v2 = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 2);
897 let ad2 = DistributedCapabilityAdvertisement::new(cap_ref_v2, "peer-b".to_string());
898 resolver.register(ad2);
899
900 let all = resolver.resolve_all("docs", "vector-search");
901 assert_eq!(all.len(), 2);
902 }
903}