farp/
lib.rs

1//! FARP - Forge API Gateway Registration Protocol
2//!
3//! FARP is a protocol specification for enabling service instances to automatically
4//! register their API schemas, health information, and capabilities with API gateways
5//! and service meshes.
6//!
7//! # Overview
8//!
9//! FARP provides:
10//! - Schema-aware service discovery (OpenAPI, AsyncAPI, gRPC, GraphQL)
11//! - Dynamic gateway configuration based on registered schemas
12//! - Multi-protocol support with extensibility
13//! - Health and telemetry integration
14//! - Backend-agnostic storage (Consul, etcd, Kubernetes, Redis, Memory)
15//! - Push and pull models for schema distribution
16//! - Zero-downtime schema updates with versioning
17//!
18//! # Basic Usage
19//!
20//! Creating a schema manifest:
21//!
22//! ```
23//! use farp::{types::*, manifest::new_manifest};
24//!
25//! let mut manifest = new_manifest("user-service", "v1.2.3", "instance-abc123");
26//! manifest.capabilities.push("rest".to_string());
27//! manifest.endpoints.health = "/health".to_string();
28//! ```
29//!
30//! # Feature Flags
31//!
32//! - `default`: Core types + memory registry
33//! - `providers-openapi`: OpenAPI provider
34//! - `providers-asyncapi`: AsyncAPI provider
35//! - `providers-grpc`: gRPC provider
36//! - `providers-graphql`: GraphQL provider
37//! - `providers-orpc`: oRPC provider
38//! - `providers-avro`: Avro provider
39//! - `providers-thrift`: Thrift provider
40//! - `providers-all`: All providers
41//! - `gateway`: Gateway client implementation
42//! - `full`: Everything enabled
43
44pub mod errors;
45pub mod manifest;
46pub mod provider;
47pub mod storage;
48pub mod types;
49pub mod version;
50
51// Registry module
52pub mod registry {
53    use crate::errors::Result;
54    use crate::types::SchemaManifest;
55    use async_trait::async_trait;
56    use serde::{Deserialize, Serialize};
57    use std::collections::HashMap;
58
59    /// Schema registry trait for managing manifests and schemas
60    #[async_trait]
61    pub trait SchemaRegistry: Send + Sync {
62        async fn register_manifest(&self, manifest: &SchemaManifest) -> Result<()>;
63        async fn get_manifest(&self, instance_id: &str) -> Result<SchemaManifest>;
64        async fn update_manifest(&self, manifest: &SchemaManifest) -> Result<()>;
65        async fn delete_manifest(&self, instance_id: &str) -> Result<()>;
66        async fn list_manifests(&self, service_name: &str) -> Result<Vec<SchemaManifest>>;
67        async fn publish_schema(&self, path: &str, schema: &serde_json::Value) -> Result<()>;
68        async fn fetch_schema(&self, path: &str) -> Result<serde_json::Value>;
69        async fn delete_schema(&self, path: &str) -> Result<()>;
70        async fn watch_manifests(
71            &self,
72            service_name: &str,
73            on_change: Box<dyn ManifestChangeHandler>,
74        ) -> Result<()>;
75        async fn watch_schemas(
76            &self,
77            path: &str,
78            on_change: Box<dyn SchemaChangeHandler>,
79        ) -> Result<()>;
80        async fn close(&self) -> Result<()>;
81        async fn health(&self) -> Result<()>;
82    }
83
84    pub trait ManifestChangeHandler: Send + Sync {
85        fn on_change(&self, event: &ManifestEvent);
86    }
87
88    impl<F> ManifestChangeHandler for F
89    where
90        F: Fn(&ManifestEvent) + Send + Sync,
91    {
92        fn on_change(&self, event: &ManifestEvent) {
93            self(event)
94        }
95    }
96
97    pub trait SchemaChangeHandler: Send + Sync {
98        fn on_change(&self, event: &SchemaEvent);
99    }
100
101    impl<F> SchemaChangeHandler for F
102    where
103        F: Fn(&SchemaEvent) + Send + Sync,
104    {
105        fn on_change(&self, event: &SchemaEvent) {
106            self(event)
107        }
108    }
109
110    #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
111    pub struct ManifestEvent {
112        pub event_type: EventType,
113        pub manifest: SchemaManifest,
114        pub timestamp: i64,
115    }
116
117    #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
118    pub struct SchemaEvent {
119        pub event_type: EventType,
120        pub path: String,
121        pub schema: Option<serde_json::Value>,
122        pub timestamp: i64,
123    }
124
125    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
126    #[serde(rename_all = "lowercase")]
127    pub enum EventType {
128        #[serde(rename = "added")]
129        Added,
130        #[serde(rename = "updated")]
131        Updated,
132        #[serde(rename = "removed")]
133        Removed,
134    }
135
136    impl std::fmt::Display for EventType {
137        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138            let s = match self {
139                EventType::Added => "added",
140                EventType::Updated => "updated",
141                EventType::Removed => "removed",
142            };
143            write!(f, "{s}")
144        }
145    }
146
147    #[derive(Debug, Clone, Serialize, Deserialize)]
148    pub struct RegistryConfig {
149        pub backend: String,
150        pub namespace: String,
151        pub backend_config: HashMap<String, serde_json::Value>,
152        pub max_schema_size: i64,
153        pub compression_threshold: i64,
154        pub ttl: i64,
155    }
156
157    impl Default for RegistryConfig {
158        fn default() -> Self {
159            Self {
160                backend: "memory".to_string(),
161                namespace: "farp".to_string(),
162                backend_config: HashMap::new(),
163                max_schema_size: 1024 * 1024,
164                compression_threshold: 100 * 1024,
165                ttl: 0,
166            }
167        }
168    }
169
170    pub trait SchemaCache: Send + Sync {
171        fn get(&self, hash: &str) -> Option<serde_json::Value>;
172        fn set(&self, hash: &str, schema: serde_json::Value) -> Result<()>;
173        fn delete(&self, hash: &str) -> Result<()>;
174        fn clear(&self) -> Result<()>;
175        fn size(&self) -> usize;
176    }
177
178    #[derive(Debug, Clone)]
179    pub struct FetchOptions {
180        pub use_cache: bool,
181        pub validate_checksum: bool,
182        pub expected_hash: Option<String>,
183        pub timeout: u64,
184    }
185
186    impl Default for FetchOptions {
187        fn default() -> Self {
188            Self {
189                use_cache: true,
190                validate_checksum: true,
191                expected_hash: None,
192                timeout: 30,
193            }
194        }
195    }
196
197    #[derive(Debug, Clone)]
198    pub struct PublishOptions {
199        pub compress: bool,
200        pub ttl: i64,
201        pub overwrite_existing: bool,
202    }
203
204    impl Default for PublishOptions {
205        fn default() -> Self {
206            Self {
207                compress: false,
208                ttl: 0,
209                overwrite_existing: true,
210            }
211        }
212    }
213
214    #[cfg(feature = "memory-registry")]
215    pub mod memory;
216}
217
218// Providers
219pub mod providers;
220
221// Gateway client
222#[cfg(feature = "gateway")]
223pub mod gateway;
224
225// Merger for OpenAPI composition
226pub mod merger;
227
228// Re-exports for convenience
229pub use errors::{Error, Result};
230pub use version::{get_version, is_compatible, PROTOCOL_VERSION};
231
232/// Prelude module for convenient imports
233pub mod prelude {
234    pub use crate::errors::{Error, Result};
235    pub use crate::manifest::*;
236    pub use crate::provider::*;
237    pub use crate::registry::SchemaRegistry;
238    pub use crate::storage::*;
239    pub use crate::types::*;
240    pub use crate::version::*;
241}