batata-client 0.0.2

Rust client for Batata/Nacos service discovery and configuration management
Documentation
# Batata Client

A Rust client library for [Nacos](https://nacos.io/) service discovery and configuration management.

[![Crates.io](https://img.shields.io/crates/v/batata-client.svg)](https://crates.io/crates/batata-client)
[![Documentation](https://docs.rs/batata-client/badge.svg)](https://docs.rs/batata-client)
[![License](https://img.shields.io/crates/l/batata-client.svg)](LICENSE)

English | [中文]README_CN.md

## Features

- **Configuration Management**
  - Get, publish, and remove configurations
  - Listen for configuration changes with callbacks
  - Search configurations with pagination
  - Local caching with MD5 validation

- **Service Discovery**
  - Register, deregister, and update service instances
  - Query service instances (all or healthy only)
  - Subscribe to service change notifications
  - Automatic heartbeat for ephemeral instances

- **Authentication**
  - Username/Password authentication
  - AccessKey/SecretKey authentication (Alibaba Cloud style)
  - Automatic token refresh

- **Enterprise Features**
  - TLS/SSL support
  - Multiple server addresses with load balancing
  - Automatic failover and retry
  - Local file cache for disaster recovery

## Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
batata-client = "0.0.1"
tokio = { version = "1", features = ["full"] }
```

## Quick Start

### Basic Usage

```rust
use batata_client::{BatataClient, Result};

#[tokio::main]
async fn main() -> Result<()> {
    // Create client
    let client = BatataClient::builder()
        .server_addr("localhost:8848")
        .namespace("public")
        .build()
        .await?;

    // Configuration management
    let config_service = client.config_service();

    // Get configuration
    let content = config_service.get_config("my-config", "DEFAULT_GROUP").await?;
    println!("Config: {}", content);

    // Publish configuration
    config_service.publish_config("my-config", "DEFAULT_GROUP", "key=value").await?;

    // Service discovery
    let naming_service = client.naming_service();

    // Register instance
    naming_service.register_instance_simple("my-service", "127.0.0.1", 8080).await?;

    // Get healthy instances
    let instances = naming_service.select_instances("my-service", "DEFAULT_GROUP", true).await?;

    // Shutdown
    client.shutdown().await;

    Ok(())
}
```

### With Authentication

```rust
use batata_client::BatataClient;

// Username/Password authentication
let client = BatataClient::builder()
    .server_addr("localhost:8848")
    .username_password("nacos", "nacos")
    .build()
    .await?;

// AccessKey/SecretKey authentication
let client = BatataClient::builder()
    .server_addr("localhost:8848")
    .access_key("your-access-key", "your-secret-key")
    .build()
    .await?;
```

### With TLS

```rust
use batata_client::{BatataClient, TlsConfig};

let client = BatataClient::builder()
    .server_addr("localhost:8848")
    .tls_config(
        TlsConfig::new()
            .with_ca_cert("/path/to/ca.pem")
            .with_client_cert("/path/to/cert.pem", "/path/to/key.pem")
    )
    .build()
    .await?;
```

### Listen for Configuration Changes

```rust
use batata_client::{BatataClient, ConfigChangeEvent};

let client = BatataClient::builder()
    .server_addr("localhost:8848")
    .build()
    .await?;

let config_service = client.config_service();

// Add listener with callback
config_service.add_callback_listener("my-config", "DEFAULT_GROUP", |event: ConfigChangeEvent| {
    println!("Config changed: {} -> {}",
        event.old_content.unwrap_or_default(),
        event.new_content);
});

// Start config service to enable listening
client.start_config_service().await?;
```

### Subscribe to Service Changes

```rust
use batata_client::{BatataClient, ServiceChangeEvent};

let client = BatataClient::builder()
    .server_addr("localhost:8848")
    .build()
    .await?;

let naming_service = client.naming_service();

// Subscribe with callback
naming_service.subscribe_callback("my-service", "DEFAULT_GROUP", |event: ServiceChangeEvent| {
    println!("Service instances changed: {:?}", event.instances);
}).await?;
```

### Search Configurations

```rust
let config_service = client.config_service();

// Search with pagination
let (total, items) = config_service
    .search_config("test*", "DEFAULT_GROUP", 1, 10)
    .await?;

println!("Found {} configs", total);
for item in items {
    println!("- {}: {}", item.data_id, item.group);
}
```

### Update Service Instance

```rust
use batata_client::Instance;

let naming_service = client.naming_service();

// Update instance weight and status
let instance = Instance::new("127.0.0.1", 8080)
    .with_weight(2.0)
    .with_enabled(false)
    .with_metadata("version", "2.0.0");

naming_service.update_instance("my-service", "DEFAULT_GROUP", instance).await?;
```

### Local Cache for Failover

```rust
let client = BatataClient::builder()
    .server_addr("localhost:8848")
    .cache_dir("/tmp/nacos-cache")
    .build()
    .await?;
```

## Configuration Options

| Option | Description | Default |
|--------|-------------|---------|
| `server_addr` | Nacos server address | `localhost:8848` |
| `server_addrs` | Multiple server addresses | - |
| `namespace` | Namespace ID | `public` |
| `app_name` | Application name | - |
| `timeout_ms` | Request timeout in milliseconds | `3000` |
| `retry_times` | Number of retry attempts | `3` |
| `username_password` | Username and password for auth | - |
| `access_key` | Access key and secret key for auth | - |
| `tls` | Enable TLS | `false` |
| `tls_config` | TLS configuration | - |
| `cache_dir` | Local cache directory | - |

## Examples

Run the examples:

```bash
# Configuration example
cargo run --example config_example

# Naming example
cargo run --example naming_example
```

## License

Apache License 2.0