batata-consul-client 0.0.1

Rust client for HashiCorp Consul or batata
Documentation
//! batata-consul-client - Rust client for HashiCorp Consul
//!
//! # Overview
//!
//! This library provides a Rust client for interacting with HashiCorp Consul,
//! supporting service discovery, health checking, key-value store, sessions,
//! ACL, and more.
//!
//! # Quick Start
//!
//! ```rust,no_run
//! use batata_consul_client::{Client, Config};
//!
//! #[tokio::main]
//! async fn main() -> batata_consul_client::Result<()> {
//!     // Create client with default configuration
//!     let client = Client::new(Config::default())?;
//!
//!     // Use KV store
//!     let kv = client.kv();
//!     kv.put_string("my/key", "my-value", None).await?;
//!
//!     let (pair, _meta) = kv.get("my/key", None).await?;
//!     if let Some(p) = pair {
//!         println!("Value: {:?}", p.value_string());
//!     }
//!
//!     // Register a service
//!     use batata_consul_client::api::agent::{AgentServiceRegistration, AgentServiceCheck};
//!
//!     let registration = AgentServiceRegistration::new("my-service")
//!         .with_id("my-service-1")
//!         .with_address("127.0.0.1")
//!         .with_port(8080)
//!         .with_check(AgentServiceCheck::http("http://127.0.0.1:8080/health", "10s"));
//!
//!     client.agent().service_register(&registration).await?;
//!
//!     // Query healthy services
//!     let (services, _meta) = client.health().service_healthy("my-service", None).await?;
//!     for service in services {
//!         println!("Service: {} at {}:{}",
//!             service.service.service,
//!             service.service.address,
//!             service.service.port
//!         );
//!     }
//!
//!     Ok(())
//! }
//! ```
//!
//! # Features
//!
//! - **KV Store**: Get, put, delete keys with CAS support and locking
//! - **Agent**: Register/deregister services, manage checks, TTL
//! - **Catalog**: Query nodes and services across the cluster
//! - **Health**: Health check queries with filtering
//! - **Session**: Distributed locking and leader election
//! - **ACL**: Token, policy, and role management

pub mod api;
pub mod client;
pub mod config;
pub mod error;
pub mod types;

pub use client::HttpClient;
pub use config::{Config, ConfigBuilder, HttpBasicAuth, TlsConfig};
pub use error::{ConsulError, Result};
pub use types::*;

use std::sync::Arc;

use api::{ACL, Agent, Catalog, Health, KV, Session};

/// Main Consul client
///
/// This is the primary interface for interacting with Consul.
/// It provides access to all API endpoints through dedicated clients.
pub struct Client {
    http_client: Arc<HttpClient>,
    kv: KV,
    agent: Agent,
    catalog: Catalog,
    health: Health,
    session: Session,
    acl: ACL,
}

impl Client {
    /// Create a new Consul client with the given configuration
    pub fn new(config: Config) -> Result<Self> {
        let http_client = Arc::new(HttpClient::new(config)?);

        Ok(Self {
            kv: KV::new(http_client.clone()),
            agent: Agent::new(http_client.clone()),
            catalog: Catalog::new(http_client.clone()),
            health: Health::new(http_client.clone()),
            session: Session::new(http_client.clone()),
            acl: ACL::new(http_client.clone()),
            http_client,
        })
    }

    /// Create a new client with configuration from environment variables
    pub fn from_env() -> Result<Self> {
        let config = Config::from_env()?;
        Self::new(config)
    }

    /// Create a new client builder
    pub fn builder() -> ClientBuilder {
        ClientBuilder::new()
    }

    /// Get the KV API client
    pub fn kv(&self) -> &KV {
        &self.kv
    }

    /// Get the Agent API client
    pub fn agent(&self) -> &Agent {
        &self.agent
    }

    /// Get the Catalog API client
    pub fn catalog(&self) -> &Catalog {
        &self.catalog
    }

    /// Get the Health API client
    pub fn health(&self) -> &Health {
        &self.health
    }

    /// Get the Session API client
    pub fn session(&self) -> &Session {
        &self.session
    }

    /// Get the ACL API client
    pub fn acl(&self) -> &ACL {
        &self.acl
    }

    /// Get the underlying HTTP client
    pub fn http_client(&self) -> &HttpClient {
        &self.http_client
    }

    /// Check if the Consul server is reachable
    pub async fn ping(&self) -> Result<()> {
        self.http_client.ping().await
    }
}

/// Builder for creating a Consul client
pub struct ClientBuilder {
    config: Config,
}

impl ClientBuilder {
    /// Create a new client builder
    pub fn new() -> Self {
        Self {
            config: Config::default(),
        }
    }

    /// Set the Consul server address
    pub fn address(mut self, address: &str) -> Self {
        self.config.address = address.to_string();
        self
    }

    /// Set the HTTP scheme (http or https)
    pub fn scheme(mut self, scheme: &str) -> Self {
        self.config.scheme = scheme.to_string();
        self
    }

    /// Set the datacenter
    pub fn datacenter(mut self, dc: &str) -> Self {
        self.config.datacenter = Some(dc.to_string());
        self
    }

    /// Set the ACL token
    pub fn token(mut self, token: &str) -> Self {
        self.config.token = Some(token.to_string());
        self
    }

    /// Set HTTP basic authentication
    pub fn http_auth(mut self, username: &str, password: &str) -> Self {
        self.config.http_auth = Some(HttpBasicAuth::new(username, password));
        self
    }

    /// Set TLS configuration
    pub fn tls_config(mut self, tls: TlsConfig) -> Self {
        self.config.tls_config = Some(tls);
        self
    }

    /// Set request timeout
    pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
        self.config.timeout = timeout;
        self
    }

    /// Set the namespace (Enterprise only)
    pub fn namespace(mut self, ns: &str) -> Self {
        self.config.namespace = Some(ns.to_string());
        self
    }

    /// Set the partition (Enterprise only)
    pub fn partition(mut self, partition: &str) -> Self {
        self.config.partition = Some(partition.to_string());
        self
    }

    /// Build the client
    pub fn build(self) -> Result<Client> {
        Client::new(self.config)
    }
}

impl Default for ClientBuilder {
    fn default() -> Self {
        Self::new()
    }
}

/// Prelude module for common imports
pub mod prelude {
    pub use crate::{
        Client, ClientBuilder, Config, ConfigBuilder, ConsulError, QueryOptions, Result, TlsConfig,
        WriteOptions,
        api::{
            acl::{ACLPolicy, ACLToken, ACLTokenPolicyLink},
            agent::{AgentServiceCheck, AgentServiceRegistration},
            catalog::{CatalogDeregistration, CatalogRegistration, CatalogServiceRegistration},
            kv::KVPair,
            session::{SessionBehavior, SessionRequest},
        },
    };
}