Files
harmony/harmony_config/examples/basic.rs
Sylvain Tremblay 08b55ee36d feat(harmony_config): unified config layer — ConfigClient, ConfigClass, masking
Net-diff PR (2 of 4) splitting feat/unified-config-and-secrets.
harmony_config + harmony_config_derive; compiles against master's harmony_secret.

- ConfigClass + #[config(secret)] derive; class plumbed through ConfigSource
- ConfigManager -> ConfigClient rename + for_namespace + Builder
- per-class secret masking: input echoes '*', output renders '****'
- get_or_prompt persists to every writable source
- SQLite dropped from the canonical chain (cleartext-at-rest) + namespaced
- prompt banner; serde-rename caveat docs; store-error logging
- docs: ADR-020-1 names OPENBAO_URL/VAULT_ADDR; ROADMAP/01 + firewall_pair rename

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:45:12 -04:00

91 lines
2.7 KiB
Rust

//! Basic example showing harmony_config with SQLite backend
//!
//! This example demonstrates:
//! - Zero-setup SQLite backend (no configuration needed)
//! - Using the `#[derive(Config)]` macro
//! - Environment variable override (HARMONY_CONFIG_TestConfig overrides SQLite)
//! - Direct set/get operations (prompting requires a TTY)
//!
//! Run with:
//! - `cargo run --example basic` - creates/reads config from SQLite
//! - `HARMONY_CONFIG_TestConfig='{"name":"from_env","count":42}' cargo run --example basic` - uses env var
use std::sync::Arc;
use harmony_config::{Config, ConfigClient, EnvSource, SqliteSource};
use log::info;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Config)]
struct TestConfig {
name: String,
count: u32,
}
impl Default for TestConfig {
fn default() -> Self {
Self {
name: "default_name".to_string(),
count: 0,
}
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
env_logger::init();
// Namespace the SQLite file so this example's state doesn't
// collide with other harmony binaries that also use SqliteSource.
let sqlite = SqliteSource::for_namespace("harmony_config-basic-example").await?;
let manager = ConfigClient::new(vec![Arc::new(EnvSource), Arc::new(sqlite)]);
info!("1. Attempting to get TestConfig (expect NotFound on first run)...");
match manager.get::<TestConfig>().await {
Ok(config) => {
info!(" Found config: {:?}", config);
}
Err(harmony_config::ConfigError::NotFound { .. }) => {
info!(" NotFound - as expected on first run");
}
Err(e) => {
info!(" Error: {:?}", e);
}
}
info!("\n2. Setting config directly...");
let config = TestConfig {
name: "from_code".to_string(),
count: 42,
};
manager.set(&config).await?;
info!(" Set config: {:?}", config);
info!("\n3. Getting config back from SQLite...");
let retrieved: TestConfig = manager.get().await?;
info!(" Retrieved: {:?}", retrieved);
info!("\n4. Using env override...");
info!(" Env var HARMONY_CONFIG_TestConfig overrides SQLite");
let env_config = TestConfig {
name: "from_env".to_string(),
count: 99,
};
unsafe {
std::env::set_var(
"HARMONY_CONFIG_TestConfig",
serde_json::to_string(&env_config)?,
);
}
let from_env: TestConfig = manager.get().await?;
info!(" Got from env: {:?}", from_env);
unsafe {
std::env::remove_var("HARMONY_CONFIG_TestConfig");
}
info!("\nDone! Config persisted at ~/.local/share/harmony/config/config.db");
Ok(())
}