### Live Configuration Reloading with File Watcher Source: https://context7.com/rust-cli/config-rs/llms.txt This example demonstrates how to combine config-rs with the `notify` crate to implement live configuration reloading. It uses a global, thread-safe `RwLock` and a file watcher to detect and apply changes without restarting the application. ```rust use config::{Config, File}; use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher}; use std::collections::HashMap; use std::path::Path; use std::sync::mpsc::channel; use std::sync::{OnceLock, RwLock}; use std::time::Duration; static SETTINGS_PATH: &str = "config/settings.toml"; // Global configuration with thread-safe access fn settings() -> &'static RwLock { static CONFIG: OnceLock> = OnceLock::new(); CONFIG.get_or_init(|| RwLock::new(load_config())) } fn load_config() -> Config { Config::builder() .add_source(File::with_name(SETTINGS_PATH)) .build() .expect("Failed to load configuration") } fn refresh_config() { *settings().write().unwrap() = load_config(); println!("Configuration reloaded"); } fn get_setting(key: &str) -> Result { settings().read().unwrap().get(key) } fn main() { // Create channel for file events let (tx, rx) = channel(); // Set up file watcher let mut watcher: RecommendedWatcher = Watcher::new( tx, notify::Config::default().with_poll_interval(Duration::from_secs(2)), ).unwrap(); watcher .watch(Path::new(SETTINGS_PATH), RecursiveMode::NonRecursive) .unwrap(); // Initial config display println!("Current settings: {:?}", settings().read().unwrap() .clone() .try_deserialize::>() ); // Watch for changes loop { match rx.recv() { Ok(Ok(Event { kind: notify::event::EventKind::Modify(_), .. })) => { refresh_config(); println!("New settings: {:?}", settings().read().unwrap() .clone() .try_deserialize::>() ); } Err(e) => println!("Watch error: {:?}", e), _ => {} } } } ``` -------------------------------- ### Load Config from Environment Variables with Prefix Source: https://context7.com/rust-cli/config-rs/llms.txt Loads configuration from environment variables, using a specified prefix to filter relevant variables. Basic usage example. ```rust use config::{Config, Environment, Case}; use serde::Deserialize; use std::collections::HashMap; #[derive(Debug, Deserialize)] struct Settings { debug: bool, database: DatabaseSettings, allowed_hosts: Vec, } #[derive(Debug, Deserialize)] struct DatabaseSettings { host: String, port: i64, } fn main() -> Result<(), config::ConfigError> { // Basic usage: APP_DEBUG=true, APP_PORT=8080 let config = Config::builder() .add_source(Environment::with_prefix("APP")) .build()?; // With separator: APP_DATABASE_HOST=localhost -> database.host let config = Config::builder() .add_source( Environment::with_prefix("APP") .separator("_") ) .build()?; // Enable type parsing (bool, int, float detection) // APP_DEBUG=true (parsed as bool), APP_PORT=8080 (parsed as i64) let config = Config::builder() .add_source( Environment::with_prefix("APP") .separator("_") .try_parsing(true) ) .build()?; // Parse comma-separated lists: APP_ALLOWED_HOSTS=host1,host2,host3 let config = Config::builder() .add_source( Environment::with_prefix("APP") .separator("_") .try_parsing(true) .list_separator(",") .with_list_parse_key("allowed_hosts") ) .build()?; // Case conversion for kebab-case struct fields let config = Config::builder() .add_source( Environment::with_prefix("APP") .separator("_") .convert_case(Case::Kebab) ) .build()?; // Ignore empty env variables let config = Config::builder() .add_source( Environment::with_prefix("APP") .ignore_empty(true) ) .build()?; // Testing: provide custom source instead of system env let mut test_env = HashMap::new(); test_env.insert("DEBUG".into(), "true".into()); test_env.insert("DATABASE_HOST".into(), "testhost".into()); let config = Config::builder() .add_source( Environment::default() .separator("_") .try_parsing(true) .source(Some(test_env)) ) .build()?; println!("Debug: {:?}", config.get_bool("debug")); Ok(()) } ``` -------------------------------- ### Implement Custom KeyValueFormat for config-rs Source: https://context7.com/rust-cli/config-rs/llms.txt This snippet shows how to implement the `Format` and `FileStoredFormat` traits to parse custom key=value configuration files. It handles basic types like booleans, integers, floats, and strings, skipping comments and empty lines. ```rust use config::{Config, File, FileStoredFormat, Format, Map, Value, ValueKind}; use std::error::Error; use std::io::{Error as IoError, ErrorKind}; // Custom format for key=value files #[derive(Debug, Clone)] struct KeyValueFormat; impl Format for KeyValueFormat { fn parse( &self, uri: Option<&String>, text: &str, ) -> Result, Box> { let mut result = Map::new(); for line in text.lines() { let line = line.trim(); // Skip empty lines and comments if line.is_empty() || line.starts_with('#') { continue; } // Parse key=value pairs if let Some((key, value)) = line.split_once('=') { let key = key.trim().to_lowercase(); let value = value.trim(); // Try to parse as number or boolean let kind = if let Ok(b) = value.parse::() { ValueKind::Boolean(b) } else if let Ok(i) = value.parse::() { ValueKind::I64(i) } else if let Ok(f) = value.parse::() { ValueKind::Float(f) } else { ValueKind::String(value.to_string()) }; result.insert(key, Value::new(uri, kind)); } } if result.is_empty() { return Err(Box::new(IoError::new( ErrorKind::InvalidData, "No valid key=value pairs found", ))); } Ok(result) } } impl FileStoredFormat for KeyValueFormat { fn file_extensions(&self) -> &'static [&'static str] { &["kv", "env", "properties"] } } fn main() -> Result<(), config::ConfigError> { let content = r#"#, "# Application configuration debug=true port=8080 host=localhost timeout=30.5 "#; let config = Config::builder() .add_source(File::from_str(content, KeyValueFormat)) .build()?; println!("Debug: {}", config.get_bool("debug")?); println!("Port: {}", config.get_int("port")?); println!("Host: {}", config.get_string("host")?); println!("Timeout: {}", config.get_float("timeout")?); Ok(()) } ``` -------------------------------- ### Load Configuration from File with File::with_name Source: https://context7.com/rust-cli/config-rs/llms.txt Utilize File::with_name() to load configuration from files, with automatic format detection based on file extensions. Files can be marked as optional using .required(false). ```rust use config::{Config, File, FileFormat}; fn main() -> Result<(), config::ConfigError> { // Auto-detect format from extension (tries .toml, .json, .yaml, etc.) let config = Config::builder() .add_source(File::with_name("config/default")) .build()?; // Explicit format specification let config = Config::builder() .add_source(File::new("config/settings", FileFormat::Toml)) .build()?; // Optional file that won't error if missing let config = Config::builder() .add_source(File::with_name("config/default")) .add_source(File::with_name("config/local").required(false)) .build()?; // Load from string content let toml_content = r#" debug = true [database] host = "localhost" port = 5432 "#; let config = Config::builder() .add_source(File::from_str(toml_content, FileFormat::Toml)) .build()?; println!("Database host: {}", config.get_string("database.host")?); println!("Database port: {}", config.get_int("database.port")?); Ok(()) } ``` -------------------------------- ### Deserialize Configuration to Typed Structs Source: https://context7.com/rust-cli/config-rs/llms.txt Use `try_deserialize()` to convert the entire configuration into a strongly-typed struct. This is recommended for production applications. Ensure all necessary structs and their fields are derived with `Deserialize`. ```rust use config::{Config, Environment, File, FileFormat}; use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct Settings { pub debug: bool, pub server: ServerSettings, pub database: DatabaseSettings, #[serde(default)] pub features: Vec, } #[derive(Debug, Deserialize)] pub struct ServerSettings { pub host: String, pub port: u16, #[serde(default = "default_workers")] pub workers: usize, } fn default_workers() -> usize { 4 } #[derive(Debug, Deserialize)] pub struct DatabaseSettings { pub url: String, pub pool_size: u32, } impl Settings { pub fn new() -> Result { let run_mode = std::env::var("RUN_MODE").unwrap_or_else(|_| "development".into()); Config::builder() // Start with defaults .set_default("debug", false)? .set_default("server.workers", 4)? // Base configuration .add_source(File::with_name("config/default")) // Environment-specific (development, production, etc.) .add_source( File::with_name(&format!("config/{}", run_mode)) .required(false) ) // Local overrides (not in version control) .add_source(File::with_name("config/local").required(false)) // Environment variables (APP_SERVER_PORT=9000) .add_source( Environment::with_prefix("APP") .separator("_") .try_parsing(true) ) .build()? .try_deserialize() } } fn main() { match Settings::new() { Ok(settings) => { println!("Debug mode: {}", settings.debug); println!("Server: {}:{}", settings.server.host, settings.server.port); println!("Workers: {}", settings.server.workers); println!("Database: {}", settings.database.url); } Err(e) => { eprintln!("Failed to load configuration: {}", e); std::process::exit(1); } } } ``` -------------------------------- ### Create Configuration Builder with Config::builder Source: https://context7.com/rust-cli/config-rs/llms.txt Use Config::builder() to create a ConfigBuilder for fluently chaining configuration sources. This includes setting defaults, adding file and environment sources, and programmatic overrides before building the final configuration. ```rust use config::{Config, Environment, File, FileFormat}; use serde::Deserialize; use std::collections::HashMap; #[derive(Debug, Deserialize)] struct AppSettings { debug: bool, database_url: String, max_connections: i64, } fn main() -> Result<(), config::ConfigError> { let settings = Config::builder() // Set default values .set_default("debug", false)? .set_default("max_connections", 10)? // Add configuration from file (auto-detects format from extension) .add_source(File::with_name("config/settings")) // Add environment-specific file (optional) .add_source(File::with_name("config/local").required(false)) // Add environment variables with APP_ prefix .add_source(Environment::with_prefix("APP")) // Programmatic override (highest priority) .set_override("debug", true)? .build()?; // Access individual values println!("Debug mode: {}", settings.get_bool("debug")?); println!("DB URL: {}", settings.get_string("database_url")?); // Deserialize entire config into struct let app: AppSettings = settings.try_deserialize()?; println!("{:?}", app); // Or deserialize to HashMap for dynamic access let map: HashMap = settings.try_deserialize()?; println!("{:?}", map); Ok(()) } ``` -------------------------------- ### Load Configuration from Async HTTP Source Source: https://context7.com/rust-cli/config-rs/llms.txt Implement the `AsyncSource` trait to load configuration from asynchronous sources like HTTP endpoints. Use `ConfigBuilder::` and `.add_async_source()` for this purpose. This allows mixing synchronous and asynchronous configuration sources. ```rust use async_trait::async_trait; use config::{AsyncSource, ConfigBuilder, ConfigError, FileFormat, Format, Map, Value, builder::AsyncState}; #[derive(Debug)] struct HttpSource { uri: String, format: FileFormat, } #[async_trait] impl AsyncSource for HttpSource { async fn collect(&self) -> Result, ConfigError> { // Fetch configuration from HTTP endpoint let response = reqwest::get(&self.uri) .await .map_err(|e| ConfigError::Foreign(Box::new(e)))?; let text = response .text() .await .map_err(|e| ConfigError::Foreign(Box::new(e)))?; // Parse using the specified format self.format .parse(Some(&self.uri), &text) .map_err(ConfigError::Foreign) } } #[tokio::main] async fn main() -> Result<(), Box> { // Build configuration with async source let config = ConfigBuilder::::default() // Can mix sync and async sources .add_source(config::File::with_name("config/default")) .add_async_source(HttpSource { uri: "http://config-server:8080/api/config".into(), format: FileFormat::Json, }) .add_source(config::Environment::with_prefix("APP")) .build() .await?; println!("Remote config value: {}", config.get::("remote_setting")?); Ok(()) } ``` -------------------------------- ### Load and Deserialize Configuration with Error Handling Source: https://context7.com/rust-cli/config-rs/llms.txt Loads configuration from a file and attempts to deserialize it into a struct. Handles various ConfigError variants like NotFound, Type, FileParse, and Foreign errors. ```rust use config::{Config, ConfigError, File, FileFormat}; use serde::Deserialize; #[derive(Debug, Deserialize)] struct DatabaseConfig { host: String, port: u16, username: String, password: String, } fn load_config() -> Result { let config = Config::builder() .add_source(File::with_name("config/database")) .build()?; config.try_deserialize() } fn main() { match load_config() { Ok(config) => { println!("Database: {}:{}", config.host, config.port); } Err(e) => { match &e { ConfigError::NotFound(key) => { eprintln!("Missing required config key: {}", key); } ConfigError::Type { key, expected, unexpected, .. } => { eprintln!( "Type error for key {:?}: expected {}, got {}", key, expected, unexpected ); } ConfigError::FileParse { uri, cause } => { eprintln!("Failed to parse {:?}: {}", uri, cause); } ConfigError::Foreign(cause) => { eprintln!("External error: {}", cause); } _ => { eprintln!("Configuration error: {}", e); } } std::process::exit(1); } } } ``` -------------------------------- ### Access Configuration Values with Type-Specific Getters Source: https://context7.com/rust-cli/config-rs/llms.txt Retrieves configuration values using type-specific getter methods like `get_bool`, `get_int`, `get_float`, and `get_string`. Supports nested paths and array indices. ```rust use config::{Config, File, FileFormat, Map, Value}; use serde::Deserialize; #[derive(Debug, Deserialize)] struct ServerConfig { host: String, port: u16, } fn main() -> Result<(), config::ConfigError> { let config = Config::builder() .add_source(File::from_str(r#" debug = true timeout = 30 ratio = 0.75 name = "myapp" [server] host = "localhost" port = 8080 [[databases]] name = "primary" url = "postgres://localhost/main" [[databases]] name = "replica" url = "postgres://localhost/replica" "#, FileFormat::Toml)) .build()?; // Type-specific getters let debug: bool = config.get_bool("debug")?; let timeout: i64 = config.get_int("timeout")?; let ratio: f64 = config.get_float("ratio")?; let name: String = config.get_string("name")?; // Nested path access with dot notation let host: String = config.get_string("server.host")?; let port: i64 = config.get_int("server.port")?; // Array index access let db_name: String = config.get_string("databases[0].name")?; let db_url: String = config.get_string("databases[1].url")?; // Generic deserialization to any type let server: ServerConfig = config.get("server")?; println!("Server: {}:{}", server.host, server.port); // Get table (map) of values let server_table: Map = config.get_table("server")?; for (key, value) in server_table { println!("{}: {}", key, value); } // Get array of values let databases: Vec = config.get_array("databases")?; println!("Number of databases: {}", databases.len()); Ok(()) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.