batata_consul_client/
lib.rs

1//! batata-consul-client - Rust client for HashiCorp Consul
2//!
3//! # Overview
4//!
5//! This library provides a Rust client for interacting with HashiCorp Consul,
6//! supporting service discovery, health checking, key-value store, sessions,
7//! ACL, and more.
8//!
9//! # Quick Start
10//!
11//! ```rust,no_run
12//! use batata_consul_client::{Client, Config};
13//!
14//! #[tokio::main]
15//! async fn main() -> batata_consul_client::Result<()> {
16//!     // Create client with default configuration
17//!     let client = Client::new(Config::default())?;
18//!
19//!     // Use KV store
20//!     let kv = client.kv();
21//!     kv.put_string("my/key", "my-value", None).await?;
22//!
23//!     let (pair, _meta) = kv.get("my/key", None).await?;
24//!     if let Some(p) = pair {
25//!         println!("Value: {:?}", p.value_string());
26//!     }
27//!
28//!     // Register a service
29//!     use batata_consul_client::api::agent::{AgentServiceRegistration, AgentServiceCheck};
30//!
31//!     let registration = AgentServiceRegistration::new("my-service")
32//!         .with_id("my-service-1")
33//!         .with_address("127.0.0.1")
34//!         .with_port(8080)
35//!         .with_check(AgentServiceCheck::http("http://127.0.0.1:8080/health", "10s"));
36//!
37//!     client.agent().service_register(&registration).await?;
38//!
39//!     // Query healthy services
40//!     let (services, _meta) = client.health().service_healthy("my-service", None).await?;
41//!     for service in services {
42//!         println!("Service: {} at {}:{}",
43//!             service.service.service,
44//!             service.service.address,
45//!             service.service.port
46//!         );
47//!     }
48//!
49//!     Ok(())
50//! }
51//! ```
52//!
53//! # Features
54//!
55//! - **KV Store**: Get, put, delete keys with CAS support and locking
56//! - **Agent**: Register/deregister services, manage checks, TTL
57//! - **Catalog**: Query nodes and services across the cluster
58//! - **Health**: Health check queries with filtering
59//! - **Session**: Distributed locking and leader election
60//! - **ACL**: Token, policy, and role management
61
62pub mod api;
63pub mod client;
64pub mod config;
65pub mod error;
66pub mod types;
67
68pub use client::HttpClient;
69pub use config::{Config, ConfigBuilder, HttpBasicAuth, TlsConfig};
70pub use error::{ConsulError, Result};
71pub use types::*;
72
73use std::sync::Arc;
74
75use api::{ACL, Agent, Catalog, Health, KV, Session};
76
77/// Main Consul client
78///
79/// This is the primary interface for interacting with Consul.
80/// It provides access to all API endpoints through dedicated clients.
81pub struct Client {
82    http_client: Arc<HttpClient>,
83    kv: KV,
84    agent: Agent,
85    catalog: Catalog,
86    health: Health,
87    session: Session,
88    acl: ACL,
89}
90
91impl Client {
92    /// Create a new Consul client with the given configuration
93    pub fn new(config: Config) -> Result<Self> {
94        let http_client = Arc::new(HttpClient::new(config)?);
95
96        Ok(Self {
97            kv: KV::new(http_client.clone()),
98            agent: Agent::new(http_client.clone()),
99            catalog: Catalog::new(http_client.clone()),
100            health: Health::new(http_client.clone()),
101            session: Session::new(http_client.clone()),
102            acl: ACL::new(http_client.clone()),
103            http_client,
104        })
105    }
106
107    /// Create a new client with configuration from environment variables
108    pub fn from_env() -> Result<Self> {
109        let config = Config::from_env()?;
110        Self::new(config)
111    }
112
113    /// Create a new client builder
114    pub fn builder() -> ClientBuilder {
115        ClientBuilder::new()
116    }
117
118    /// Get the KV API client
119    pub fn kv(&self) -> &KV {
120        &self.kv
121    }
122
123    /// Get the Agent API client
124    pub fn agent(&self) -> &Agent {
125        &self.agent
126    }
127
128    /// Get the Catalog API client
129    pub fn catalog(&self) -> &Catalog {
130        &self.catalog
131    }
132
133    /// Get the Health API client
134    pub fn health(&self) -> &Health {
135        &self.health
136    }
137
138    /// Get the Session API client
139    pub fn session(&self) -> &Session {
140        &self.session
141    }
142
143    /// Get the ACL API client
144    pub fn acl(&self) -> &ACL {
145        &self.acl
146    }
147
148    /// Get the underlying HTTP client
149    pub fn http_client(&self) -> &HttpClient {
150        &self.http_client
151    }
152
153    /// Check if the Consul server is reachable
154    pub async fn ping(&self) -> Result<()> {
155        self.http_client.ping().await
156    }
157}
158
159/// Builder for creating a Consul client
160pub struct ClientBuilder {
161    config: Config,
162}
163
164impl ClientBuilder {
165    /// Create a new client builder
166    pub fn new() -> Self {
167        Self {
168            config: Config::default(),
169        }
170    }
171
172    /// Set the Consul server address
173    pub fn address(mut self, address: &str) -> Self {
174        self.config.address = address.to_string();
175        self
176    }
177
178    /// Set the HTTP scheme (http or https)
179    pub fn scheme(mut self, scheme: &str) -> Self {
180        self.config.scheme = scheme.to_string();
181        self
182    }
183
184    /// Set the datacenter
185    pub fn datacenter(mut self, dc: &str) -> Self {
186        self.config.datacenter = Some(dc.to_string());
187        self
188    }
189
190    /// Set the ACL token
191    pub fn token(mut self, token: &str) -> Self {
192        self.config.token = Some(token.to_string());
193        self
194    }
195
196    /// Set HTTP basic authentication
197    pub fn http_auth(mut self, username: &str, password: &str) -> Self {
198        self.config.http_auth = Some(HttpBasicAuth::new(username, password));
199        self
200    }
201
202    /// Set TLS configuration
203    pub fn tls_config(mut self, tls: TlsConfig) -> Self {
204        self.config.tls_config = Some(tls);
205        self
206    }
207
208    /// Set request timeout
209    pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
210        self.config.timeout = timeout;
211        self
212    }
213
214    /// Set the namespace (Enterprise only)
215    pub fn namespace(mut self, ns: &str) -> Self {
216        self.config.namespace = Some(ns.to_string());
217        self
218    }
219
220    /// Set the partition (Enterprise only)
221    pub fn partition(mut self, partition: &str) -> Self {
222        self.config.partition = Some(partition.to_string());
223        self
224    }
225
226    /// Build the client
227    pub fn build(self) -> Result<Client> {
228        Client::new(self.config)
229    }
230}
231
232impl Default for ClientBuilder {
233    fn default() -> Self {
234        Self::new()
235    }
236}
237
238/// Prelude module for common imports
239pub mod prelude {
240    pub use crate::{
241        Client, ClientBuilder, Config, ConfigBuilder, ConsulError, QueryOptions, Result, TlsConfig,
242        WriteOptions,
243        api::{
244            acl::{ACLPolicy, ACLToken, ACLTokenPolicyLink},
245            agent::{AgentServiceCheck, AgentServiceRegistration},
246            catalog::{CatalogDeregistration, CatalogRegistration, CatalogServiceRegistration},
247            kv::KVPair,
248            session::{SessionBehavior, SessionRequest},
249        },
250    };
251}