use crate::Operation;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct CapabilityId(pub String);
impl fmt::Display for CapabilityId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl CapabilityId {
pub fn new(id: String) -> Self {
CapabilityId(id)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum CapabilityRole {
ExecutionEngine,
Projection,
ExecutionWithProjection,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum CapabilityLifecycleState {
Registered,
Initializing,
CatchingUp,
Active,
Paused,
Rebuilding,
Removed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityContext {
pub capability_id: CapabilityId,
pub instance_id: String,
pub sequence: u64,
pub version: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityQuery {
pub query_type: String,
pub params: Value,
}
impl CapabilityQuery {
pub fn new(query_type: String, params: Value) -> Self {
Self { query_type, params }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityCheckpoint {
pub capability_id: CapabilityId,
pub last_processed_instance_id: String,
pub last_processed_sequence: u64,
pub lifecycle_state: CapabilityLifecycleState,
pub checkpoint_ms: u128,
pub version: u32,
}
impl CapabilityCheckpoint {
pub fn new(capability_id: CapabilityId, instance_id: String) -> Self {
Self {
capability_id,
last_processed_instance_id: instance_id,
last_processed_sequence: 0,
lifecycle_state: CapabilityLifecycleState::Registered,
checkpoint_ms: crate::now_ms(),
version: 1,
}
}
pub fn update_position(&mut self, instance_id: String, sequence: u64) {
self.last_processed_instance_id = instance_id;
self.last_processed_sequence = sequence;
self.checkpoint_ms = crate::now_ms();
}
pub fn set_state(&mut self, state: CapabilityLifecycleState) {
self.lifecycle_state = state;
self.checkpoint_ms = crate::now_ms();
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityOperationResult {
pub success: bool,
pub error: Option<String>,
pub result: Option<Value>,
pub produced_derived_state: bool,
}
impl CapabilityOperationResult {
pub fn success() -> Self {
Self {
success: true,
error: None,
result: None,
produced_derived_state: false,
}
}
pub fn with_derived_state(result: Value) -> Self {
Self {
success: true,
error: None,
result: Some(result),
produced_derived_state: true,
}
}
pub fn error(msg: String) -> Self {
Self {
success: false,
error: Some(msg),
result: None,
produced_derived_state: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityQueryResult {
pub success: bool,
pub results: Vec<Value>,
pub error: Option<String>,
}
impl CapabilityQueryResult {
pub fn success(results: Vec<Value>) -> Self {
Self {
success: true,
results,
error: None,
}
}
pub fn empty() -> Self {
Self {
success: true,
results: Vec::new(),
error: None,
}
}
pub fn error(msg: String) -> Self {
Self {
success: false,
results: Vec::new(),
error: Some(msg),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityAccessModel {
pub reads: Vec<String>,
pub writes: Vec<String>,
pub executes: Vec<String>,
}
impl CapabilityAccessModel {
pub fn new(reads: Vec<String>, writes: Vec<String>, executes: Vec<String>) -> Self {
Self {
reads,
writes,
executes,
}
}
pub fn can_read(&self, collection: &str) -> bool {
self.reads.contains(&collection.to_string()) || self.reads.iter().any(|r| r == "*")
}
pub fn can_write(&self, collection: &str) -> bool {
self.writes.contains(&collection.to_string()) || self.writes.iter().any(|w| w == "*")
}
pub fn can_execute(&self, system: &str) -> bool {
self.executes.contains(&system.to_string()) || self.executes.iter().any(|e| e == "*")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityMetadata {
pub id: CapabilityId,
pub name: String,
pub description: String,
pub role: CapabilityRole,
pub access_model: CapabilityAccessModel,
pub version: u32,
pub created_ms: u128,
}
impl CapabilityMetadata {
pub fn new(
id: CapabilityId,
name: String,
description: String,
role: CapabilityRole,
access_model: CapabilityAccessModel,
) -> Self {
Self {
id,
name,
description,
role,
access_model,
version: 1,
created_ms: crate::now_ms(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityRegistry {
#[serde(default)]
capabilities: HashMap<CapabilityId, CapabilityMetadata>,
#[serde(default)]
checkpoints: HashMap<CapabilityId, CapabilityCheckpoint>,
}
impl CapabilityRegistry {
pub fn new() -> Self {
Self {
capabilities: HashMap::new(),
checkpoints: HashMap::new(),
}
}
pub fn register(&mut self, metadata: CapabilityMetadata) -> crate::Result<()> {
let id = metadata.id.clone();
if self.capabilities.contains_key(&id) {
return Err(crate::FlowError::CapabilityError(
"Capability already registered".to_string(),
));
}
let checkpoint = CapabilityCheckpoint::new(id.clone(), String::new());
self.capabilities.insert(id.clone(), metadata);
self.checkpoints.insert(id, checkpoint);
Ok(())
}
pub fn get(&self, id: &CapabilityId) -> Option<&CapabilityMetadata> {
self.capabilities.get(id)
}
pub fn get_checkpoint(&self, id: &CapabilityId) -> Option<&CapabilityCheckpoint> {
self.checkpoints.get(id)
}
pub fn update_checkpoint(&mut self, checkpoint: CapabilityCheckpoint) {
self.checkpoints
.insert(checkpoint.capability_id.clone(), checkpoint);
}
pub fn list(&self) -> Vec<&CapabilityMetadata> {
self.capabilities.values().collect()
}
pub fn remove(&mut self, id: &CapabilityId) -> Option<CapabilityMetadata> {
self.checkpoints.remove(id);
self.capabilities.remove(id)
}
}
impl Default for CapabilityRegistry {
fn default() -> Self {
Self::new()
}
}
pub trait FlowCapability: Send + Sync {
fn id(&self) -> CapabilityId;
fn metadata(&self) -> CapabilityMetadata;
fn initialize(&mut self, context: &CapabilityContext) -> crate::Result<()>;
fn on_operation(
&mut self,
operation: &Operation,
context: &CapabilityContext,
) -> crate::Result<CapabilityOperationResult>;
fn query(
&self,
query: &CapabilityQuery,
context: &CapabilityContext,
) -> crate::Result<CapabilityQueryResult>;
fn rebuild(
&mut self,
operations: &[Operation],
context: &CapabilityContext,
) -> crate::Result<()>;
fn pause(&mut self) -> crate::Result<()> {
Ok(())
}
fn resume(&mut self) -> crate::Result<()> {
Ok(())
}
fn is_ready(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct CapabilityRef {
pub namespace: String,
pub capability: String,
pub version: u32,
}
impl CapabilityRef {
pub fn new(namespace: String, capability: String, version: u32) -> Self {
Self {
namespace,
capability,
version,
}
}
pub fn canonical(&self) -> String {
format!(
"flow://capability/{}/{}@{}",
self.namespace, self.capability, self.version
)
}
pub fn from_canonical(canonical: &str) -> crate::Result<Self> {
if !canonical.starts_with("flow://capability/") {
return Err(crate::FlowError::CapabilityError(
"Invalid capability reference format".to_string(),
));
}
let rest = &canonical[18..]; let parts: Vec<&str> = rest.split('/').collect();
if parts.len() != 2 {
return Err(crate::FlowError::CapabilityError(
"Invalid capability reference format".to_string(),
));
}
let namespace = parts[0].to_string();
let cap_parts: Vec<&str> = parts[1].split('@').collect();
if cap_parts.len() != 2 {
return Err(crate::FlowError::CapabilityError(
"Invalid capability reference format".to_string(),
));
}
let capability = cap_parts[0].to_string();
let version = cap_parts[1]
.parse::<u32>()
.map_err(|_| crate::FlowError::CapabilityError("Invalid version number".to_string()))?;
Ok(Self::new(namespace, capability, version))
}
}
impl fmt::Display for CapabilityRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.canonical())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct StateRequirement {
pub namespace: String,
pub min_version: u64,
}
impl StateRequirement {
pub fn new(namespace: String, min_version: u64) -> Self {
Self {
namespace,
min_version,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityRequirements {
pub required_state: Vec<StateRequirement>,
pub max_state_latency_ms: Option<u64>,
pub requires_ownership: bool,
}
impl CapabilityRequirements {
pub fn new() -> Self {
Self {
required_state: Vec::new(),
max_state_latency_ms: None,
requires_ownership: false,
}
}
pub fn with_required_state(mut self, requirement: StateRequirement) -> Self {
self.required_state.push(requirement);
self
}
pub fn with_max_latency(mut self, latency_ms: u64) -> Self {
self.max_state_latency_ms = Some(latency_ms);
self
}
pub fn with_ownership_requirement(mut self, requires: bool) -> Self {
self.requires_ownership = requires;
self
}
}
impl Default for CapabilityRequirements {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DistributedCapabilityAdvertisement {
pub capability_ref: CapabilityRef,
pub peer_id: String,
pub requirements: CapabilityRequirements,
pub advertised_ms: u128,
}
impl DistributedCapabilityAdvertisement {
pub fn new(capability_ref: CapabilityRef, peer_id: String) -> Self {
Self {
capability_ref,
peer_id,
requirements: CapabilityRequirements::new(),
advertised_ms: crate::now_ms(),
}
}
pub fn with_requirements(mut self, requirements: CapabilityRequirements) -> Self {
self.requirements = requirements;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityExecutionContext {
pub execution_id: String,
pub capability_ref: CapabilityRef,
pub input_refs: Vec<String>,
pub required_state_version: u64,
pub requester: String,
pub requested_ms: u128,
}
impl CapabilityExecutionContext {
pub fn new(execution_id: String, capability_ref: CapabilityRef, requester: String) -> Self {
Self {
execution_id,
capability_ref,
input_refs: Vec::new(),
required_state_version: 0,
requester,
requested_ms: crate::now_ms(),
}
}
pub fn with_input_ref(mut self, input_ref: String) -> Self {
self.input_refs.push(input_ref);
self
}
pub fn with_required_state_version(mut self, version: u64) -> Self {
self.required_state_version = version;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionRef {
pub execution_id: String,
pub capability_ref: CapabilityRef,
pub provider_peer: String,
pub input_refs: Vec<String>,
pub input_state_version: u64,
pub result_content_identity: String,
pub created_operation: Option<String>,
}
impl ExecutionRef {
pub fn new(execution_id: String, capability_ref: CapabilityRef, provider_peer: String) -> Self {
Self {
execution_id,
capability_ref,
provider_peer,
input_refs: Vec::new(),
input_state_version: 0,
result_content_identity: String::new(),
created_operation: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapabilityExecutionResult {
pub execution_ref: ExecutionRef,
pub result: Value,
pub completed_ms: u128,
pub error: Option<String>,
}
impl CapabilityExecutionResult {
pub fn success(execution_ref: ExecutionRef, result: Value) -> Self {
Self {
execution_ref,
result,
completed_ms: crate::now_ms(),
error: None,
}
}
pub fn error(execution_ref: ExecutionRef, error: String) -> Self {
Self {
execution_ref,
result: Value::Null,
completed_ms: crate::now_ms(),
error: Some(error),
}
}
}
pub struct CapabilityResolver {
capabilities: HashMap<String, DistributedCapabilityAdvertisement>,
}
impl CapabilityResolver {
pub fn new() -> Self {
Self {
capabilities: HashMap::new(),
}
}
pub fn register(&mut self, ad: DistributedCapabilityAdvertisement) {
self.capabilities.insert(ad.capability_ref.canonical(), ad);
}
pub fn resolve(&self, cap_ref: &CapabilityRef) -> Option<&DistributedCapabilityAdvertisement> {
self.capabilities.get(&cap_ref.canonical())
}
pub fn resolve_all(
&self,
namespace: &str,
capability: &str,
) -> Vec<&DistributedCapabilityAdvertisement> {
self.capabilities
.values()
.filter(|ad| {
ad.capability_ref.namespace == namespace
&& ad.capability_ref.capability == capability
})
.collect()
}
}
impl Default for CapabilityResolver {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_capability_id_creation() {
let id = CapabilityId::new("search".to_string());
assert_eq!(id.0, "search");
}
#[test]
fn test_capability_checkpoint() {
let mut checkpoint = CapabilityCheckpoint::new(
CapabilityId::new("search".to_string()),
"instance-1".to_string(),
);
assert_eq!(checkpoint.last_processed_sequence, 0);
checkpoint.update_position("instance-1".to_string(), 100);
assert_eq!(checkpoint.last_processed_sequence, 100);
}
#[test]
fn test_capability_access_model() {
let access = CapabilityAccessModel::new(
vec!["docs".to_string()],
vec!["docs.search".to_string()],
vec!["embedding-api".to_string()],
);
assert!(access.can_read("docs"));
assert!(!access.can_read("users"));
assert!(access.can_write("docs.search"));
assert!(access.can_execute("embedding-api"));
}
#[test]
fn test_capability_access_wildcard() {
let access =
CapabilityAccessModel::new(vec!["*".to_string()], vec!["*".to_string()], vec![]);
assert!(access.can_read("anything"));
assert!(access.can_write("anything"));
}
#[test]
fn test_capability_registry() {
let mut registry = CapabilityRegistry::new();
let metadata = CapabilityMetadata::new(
CapabilityId::new("search".to_string()),
"Search Capability".to_string(),
"Full-text search".to_string(),
CapabilityRole::Projection,
CapabilityAccessModel::new(
vec!["docs".to_string()],
vec!["docs.search".to_string()],
vec![],
),
);
registry.register(metadata).unwrap();
let cap_id = CapabilityId::new("search".to_string());
assert!(registry.get(&cap_id).is_some());
assert!(registry.get_checkpoint(&cap_id).is_some());
}
#[test]
fn test_capability_ref_creation_and_canonical() {
let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
assert_eq!(cap_ref.namespace, "docs");
assert_eq!(cap_ref.capability, "vector-search");
assert_eq!(cap_ref.version, 1);
assert_eq!(
cap_ref.canonical(),
"flow://capability/docs/vector-search@1"
);
}
#[test]
fn test_capability_ref_parse_canonical() {
let canonical = "flow://capability/docs/vector-search@1";
let cap_ref = CapabilityRef::from_canonical(canonical).unwrap();
assert_eq!(cap_ref.namespace, "docs");
assert_eq!(cap_ref.capability, "vector-search");
assert_eq!(cap_ref.version, 1);
}
#[test]
fn test_capability_ref_display() {
let cap_ref = CapabilityRef::new("images".to_string(), "image-analysis".to_string(), 2);
assert_eq!(
cap_ref.to_string(),
"flow://capability/images/image-analysis@2"
);
}
#[test]
fn test_state_requirement_creation() {
let req = StateRequirement::new("docs".to_string(), 847);
assert_eq!(req.namespace, "docs");
assert_eq!(req.min_version, 847);
}
#[test]
fn test_capability_requirements_builder() {
let reqs = CapabilityRequirements::new()
.with_required_state(StateRequirement::new("docs".to_string(), 847))
.with_max_latency(5000)
.with_ownership_requirement(true);
assert_eq!(reqs.required_state.len(), 1);
assert_eq!(reqs.max_state_latency_ms, Some(5000));
assert!(reqs.requires_ownership);
}
#[test]
fn test_distributed_capability_advertisement() {
let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
let ad = DistributedCapabilityAdvertisement::new(cap_ref.clone(), "peer-b".to_string())
.with_requirements(
CapabilityRequirements::new()
.with_required_state(StateRequirement::new("docs".to_string(), 847)),
);
assert_eq!(ad.capability_ref, cap_ref);
assert_eq!(ad.peer_id, "peer-b");
assert_eq!(ad.requirements.required_state.len(), 1);
}
#[test]
fn test_capability_execution_context() {
let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
let ctx = CapabilityExecutionContext::new(
"exec-123".to_string(),
cap_ref.clone(),
"requester".to_string(),
)
.with_input_ref("flow://docs#123".to_string())
.with_required_state_version(851);
assert_eq!(ctx.execution_id, "exec-123");
assert_eq!(ctx.capability_ref, cap_ref);
assert_eq!(ctx.input_refs.len(), 1);
assert_eq!(ctx.required_state_version, 851);
}
#[test]
fn test_execution_ref_creation() {
let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
let exec_ref = ExecutionRef::new(
"exec-123".to_string(),
cap_ref.clone(),
"peer-b".to_string(),
);
assert_eq!(exec_ref.execution_id, "exec-123");
assert_eq!(exec_ref.capability_ref, cap_ref);
assert_eq!(exec_ref.provider_peer, "peer-b");
}
#[test]
fn test_capability_execution_result_success() {
let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
let exec_ref = ExecutionRef::new("exec-123".to_string(), cap_ref, "peer-b".to_string());
let result = serde_json::json!({"results": [1, 2, 3]});
let exec_result = CapabilityExecutionResult::success(exec_ref.clone(), result.clone());
assert!(exec_result.error.is_none());
assert_eq!(exec_result.result, result);
}
#[test]
fn test_capability_resolver_registration() {
let mut resolver = CapabilityResolver::new();
let cap_ref = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
let ad = DistributedCapabilityAdvertisement::new(cap_ref.clone(), "peer-b".to_string());
resolver.register(ad);
let resolved = resolver.resolve(&cap_ref);
assert!(resolved.is_some());
assert_eq!(resolved.unwrap().peer_id, "peer-b");
}
#[test]
fn test_capability_resolver_resolve_all() {
let mut resolver = CapabilityResolver::new();
let cap_ref_v1 = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 1);
let ad1 = DistributedCapabilityAdvertisement::new(cap_ref_v1, "peer-a".to_string());
resolver.register(ad1);
let cap_ref_v2 = CapabilityRef::new("docs".to_string(), "vector-search".to_string(), 2);
let ad2 = DistributedCapabilityAdvertisement::new(cap_ref_v2, "peer-b".to_string());
resolver.register(ad2);
let all = resolver.resolve_all("docs", "vector-search");
assert_eq!(all.len(), 2);
}
}