revoke-config 0.3.0

Configuration management with hot-reloading for Revoke framework
Documentation
# revoke-config

Configuration management module for the Revoke microservices framework, supporting multiple backend stores and real-time configuration watching.

## Features

- **Multiple Backends**: Memory, file, and Consul backends, enabled via feature flags
- **Format Auto-detection**: Automatically detects and parses JSON, YAML, and TOML files
- **Real-time Watching**: Watch configuration changes through streaming updates
- **Version Tracking**: Built-in configuration change version tracking
- **Metadata Support**: Attach metadata to configuration items (creation time, update time, tags, etc.)
- **Type Safety**: Strongly typed configuration values using serde serialization
- **Async Support**: Fully async API based on Tokio

## Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
revoke-config = { version = "0.1", features = ["memory", "file", "consul"] }
```

## Feature Flags

- `memory` (default): In-memory configuration provider
- `file`: File-based configuration with auto-reload support
- `consul`: Consul KV store integration
- `etcd`: etcd integration (requires protoc)
- `full`: Enable all features

## Usage

### Memory Provider

Simple in-memory configuration storage:

```rust
use revoke_config::MemoryConfigProvider;
use revoke_core::ConfigProvider;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let provider = MemoryConfigProvider::new();
    
    // Set configuration value
    provider.set("app.name", "my-service").await?;
    
    // Get configuration value
    let name = provider.get("app.name").await?;
    println!("App name: {}", name);
    
    Ok(())
}
```

With initial values:

```rust
use std::collections::HashMap;
use serde_json::json;

let mut initial = HashMap::new();
initial.insert("app.port".to_string(), json!(8080));
initial.insert("app.debug".to_string(), json!(true));

let provider = MemoryConfigProvider::with_initial_values(initial);
```

### File Provider

File-based configuration with automatic format detection:

```rust
use revoke_config::FileConfigProvider;
use revoke_core::ConfigProvider;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Auto-detect format from file extension
    let provider = FileConfigProvider::new("config.yaml").await?;
    
    // Or specify format explicitly
    let provider = FileConfigProvider::with_format(
        "config.conf", 
        ConfigFormat::Json
    ).await?;
    
    // Read configuration
    let db_url = provider.get("database.url").await?;
    
    // Update configuration (saves to file)
    provider.set("database.pool_size", "10").await?;
    
    Ok(())
}
```

Enable file watching (requires `notify` feature):

```rust
#[cfg(feature = "notify")]
{
    provider.start_file_watcher().await?;
    // File changes will automatically reload configuration
}
```

### Consul Provider

Integration with Consul KV store:

```rust
use revoke_config::{ConsulConfigProvider, ConsulConfigOptions};
use revoke_core::ConfigProvider;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let options = ConsulConfigOptions {
        address: "http://localhost:8500".to_string(),
        namespace: Some("myapp".to_string()),
        watch_interval: Duration::from_secs(5),
        ..Default::default()
    };
    
    let provider = ConsulConfigProvider::new(options);
    
    // Start background watching
    provider.start_watch_loop().await;
    
    // Use configuration
    provider.set("feature.enabled", "true").await?;
    let enabled = provider.get("feature.enabled").await?;
    
    Ok(())
}
```

### Configuration Watching

Watch for real-time configuration changes:

```rust
use futures::StreamExt;
use revoke_core::ConfigProvider;

let mut stream = provider.watch("app.feature_flags").await?;

tokio::spawn(async move {
    while let Some(value) = stream.next().await {
        println!("Configuration changed: {}", value);
        // React to configuration changes
    }
});
```

### Configuration Formats

The file provider supports multiple formats:

**JSON:**
```json
{
  "app": {
    "name": "my-service",
    "port": 8080
  }
}
```

**YAML:**
```yaml
app:
  name: my-service
  port: 8080
```

**TOML:**
```toml
[app]
name = "my-service"
port = 8080
```

## Advanced Usage

### Custom Configuration Listener

```rust
use revoke_config::ConfigWatcher;
use std::sync::Arc;

let watcher = ConfigWatcher::new();

// Subscribe to changes
watcher.subscribe(Arc::new(|change| {
    println!("Config changed: {} -> {:?}", change.key, change.new_value);
}));

// Notify changes
watcher.notify(ConfigChange {
    key: "app.version".to_string(),
    old_value: Some(json!("1.0.0")),
    new_value: Some(json!("1.1.0")),
    change_type: ChangeType::Updated,
});
```

### Configuration Metadata

```rust
use revoke_config::{ConfigValue, ConfigMetadata};
use chrono::Utc;

let value = ConfigValue {
    key: "app.name".to_string(),
    value: json!("my-service"),
    version: 1,
    metadata: ConfigMetadata {
        created_at: Utc::now(),
        updated_at: Utc::now(),
        created_by: Some("admin".to_string()),
        description: Some("Application name".to_string()),
        tags: vec!["core".to_string(), "required".to_string()],
    },
};
```

### Configuration Validation

```rust
use serde::{Deserialize, Serialize};
use revoke_config::ConfigValidator;

#[derive(Debug, Deserialize, Serialize)]
struct AppConfig {
    name: String,
    port: u16,
    #[serde(default)]
    debug: bool,
}

// Validate configuration
let json_value = provider.get("app").await?;
let config: AppConfig = serde_json::from_str(&json_value)?;

if config.port < 1024 {
    return Err("Port must be >= 1024".into());
}
```

## Best Practices

1. **Namespace Configuration**: Use dot notation for hierarchical configuration
2. **Watch Granularity**: Watch specific keys rather than entire configuration
3. **Error Handling**: Always handle configuration missing or parsing errors
4. **Caching**: Providers include built-in caching for performance
5. **Atomic Updates**: Use transactions when updating multiple related values

## Performance Considerations

- **Memory Provider**: O(1) lookups, suitable for frequently accessed configuration
- **File Provider**: Cached in memory, file I/O only on changes
- **Consul Provider**: Network calls with local caching, configurable refresh interval

## Error Handling

All providers return `Result<T, RevokeError>` with specific error types:

```rust
match provider.get("app.name").await {
    Ok(value) => println!("Value: {}", value),
    Err(RevokeError::ConfigError(msg)) => eprintln!("Config error: {}", msg),
    Err(e) => eprintln!("Other error: {}", e),
}
```

## Security

1. **Sensitive Data**: Use encryption for sensitive configuration values
2. **Access Control**: Consul provider supports ACL tokens
3. **Audit Trail**: Metadata tracks who changed configuration
4. **Environment Variables**: Support for environment variable substitution

## Examples

See the [examples](examples/) directory for complete examples:

- `memory_provider.rs` - Basic memory configuration
- `file_provider.rs` - File-based configuration with watching
- `consul_provider.rs` - Consul integration
- `config_validation.rs` - Configuration validation patterns