Skip to main content

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//! - `discovery`: Service discovery abstractions
43//! - `full`: Everything enabled
44
45pub mod errors;
46pub mod manifest;
47pub mod provider;
48pub mod storage;
49pub mod types;
50pub mod version;
51
52// Discovery module
53pub mod discovery;
54
55// Registry module
56pub mod registry {
57    use crate::errors::Result;
58    use crate::types::SchemaManifest;
59    use async_trait::async_trait;
60    use serde::{Deserialize, Serialize};
61    use std::collections::HashMap;
62
63    /// Schema registry trait for managing manifests and schemas
64    #[async_trait]
65    pub trait SchemaRegistry: Send + Sync {
66        async fn register_manifest(&self, manifest: &SchemaManifest) -> Result<()>;
67        async fn get_manifest(&self, instance_id: &str) -> Result<SchemaManifest>;
68        async fn update_manifest(&self, manifest: &SchemaManifest) -> Result<()>;
69        async fn delete_manifest(&self, instance_id: &str) -> Result<()>;
70        async fn list_manifests(&self, service_name: &str) -> Result<Vec<SchemaManifest>>;
71        async fn publish_schema(&self, path: &str, schema: &serde_json::Value) -> Result<()>;
72        async fn fetch_schema(&self, path: &str) -> Result<serde_json::Value>;
73        async fn delete_schema(&self, path: &str) -> Result<()>;
74        async fn watch_manifests(
75            &self,
76            service_name: &str,
77            on_change: Box<dyn ManifestChangeHandler>,
78        ) -> Result<()>;
79        async fn watch_schemas(
80            &self,
81            path: &str,
82            on_change: Box<dyn SchemaChangeHandler>,
83        ) -> Result<()>;
84        async fn close(&self) -> Result<()>;
85        async fn health(&self) -> Result<()>;
86    }
87
88    pub trait ManifestChangeHandler: Send + Sync {
89        fn on_change(&self, event: &ManifestEvent);
90    }
91
92    impl<F> ManifestChangeHandler for F
93    where
94        F: Fn(&ManifestEvent) + Send + Sync,
95    {
96        fn on_change(&self, event: &ManifestEvent) {
97            self(event)
98        }
99    }
100
101    pub trait SchemaChangeHandler: Send + Sync {
102        fn on_change(&self, event: &SchemaEvent);
103    }
104
105    impl<F> SchemaChangeHandler for F
106    where
107        F: Fn(&SchemaEvent) + Send + Sync,
108    {
109        fn on_change(&self, event: &SchemaEvent) {
110            self(event)
111        }
112    }
113
114    #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
115    pub struct ManifestEvent {
116        pub event_type: EventType,
117        pub manifest: SchemaManifest,
118        pub timestamp: i64,
119    }
120
121    #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
122    pub struct SchemaEvent {
123        pub event_type: EventType,
124        pub path: String,
125        pub schema: Option<serde_json::Value>,
126        pub timestamp: i64,
127    }
128
129    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
130    #[serde(rename_all = "lowercase")]
131    pub enum EventType {
132        #[serde(rename = "added")]
133        Added,
134        #[serde(rename = "updated")]
135        Updated,
136        #[serde(rename = "removed")]
137        Removed,
138    }
139
140    impl std::fmt::Display for EventType {
141        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142            let s = match self {
143                EventType::Added => "added",
144                EventType::Updated => "updated",
145                EventType::Removed => "removed",
146            };
147            write!(f, "{s}")
148        }
149    }
150
151    #[derive(Debug, Clone, Serialize, Deserialize)]
152    pub struct RegistryConfig {
153        pub backend: String,
154        pub namespace: String,
155        pub backend_config: HashMap<String, serde_json::Value>,
156        pub max_schema_size: i64,
157        pub compression_threshold: i64,
158        pub ttl: i64,
159    }
160
161    impl Default for RegistryConfig {
162        fn default() -> Self {
163            Self {
164                backend: "memory".to_string(),
165                namespace: "farp".to_string(),
166                backend_config: HashMap::new(),
167                max_schema_size: 1024 * 1024,
168                compression_threshold: 100 * 1024,
169                ttl: 0,
170            }
171        }
172    }
173
174    pub trait SchemaCache: Send + Sync {
175        fn get(&self, hash: &str) -> Option<serde_json::Value>;
176        fn set(&self, hash: &str, schema: serde_json::Value) -> Result<()>;
177        fn delete(&self, hash: &str) -> Result<()>;
178        fn clear(&self) -> Result<()>;
179        fn size(&self) -> usize;
180    }
181
182    #[derive(Debug, Clone)]
183    pub struct FetchOptions {
184        pub use_cache: bool,
185        pub validate_checksum: bool,
186        pub expected_hash: Option<String>,
187        pub timeout: u64,
188    }
189
190    impl Default for FetchOptions {
191        fn default() -> Self {
192            Self {
193                use_cache: true,
194                validate_checksum: true,
195                expected_hash: None,
196                timeout: 30,
197            }
198        }
199    }
200
201    #[derive(Debug, Clone)]
202    pub struct PublishOptions {
203        pub compress: bool,
204        pub ttl: i64,
205        pub overwrite_existing: bool,
206    }
207
208    impl Default for PublishOptions {
209        fn default() -> Self {
210            Self {
211                compress: false,
212                ttl: 0,
213                overwrite_existing: true,
214            }
215        }
216    }
217
218    #[cfg(feature = "memory-registry")]
219    pub mod memory;
220}
221
222// Providers
223pub mod providers;
224
225// Gateway client
226#[cfg(feature = "gateway")]
227pub mod gateway;
228
229// Merger for OpenAPI composition
230pub mod merger;
231
232// Re-exports for convenience
233pub use errors::{Error, Result};
234pub use version::{get_version, is_compatible, PROTOCOL_VERSION};
235
236/// Prelude module for convenient imports
237pub mod prelude {
238    pub use crate::errors::{Error, Result};
239    pub use crate::manifest::*;
240    pub use crate::provider::*;
241    pub use crate::registry::SchemaRegistry;
242    pub use crate::storage::*;
243    pub use crate::types::*;
244    pub use crate::version::*;
245}