lib.rs
1 // SPDX-License-Identifier: MIT 2 // Copyright (c) 2025 kingananas20 3 4 //! # konfik 5 //! 6 //! A flexible and composable configuration parser for Rust applications that supports multiple sources and formats. 7 //! 8 //! ## Features 9 //! 10 //! - 🔧 **Multiple Sources**: Load configuration from files, environment variables, and CLI arguments 11 //! - 📁 **Multiple Formats**: Support for JSON, YAML, and TOML configuration files 12 //! - 🎯 **Priority System**: CLI args > Environment variables > Config files 13 //! - ✅ **Validation**: Custom validation functions for your configuration 14 //! - 🚀 **Zero Config**: Works out of the box with sensible defaults 15 //! - 📦 **Derive Macro**: Simple `#[derive(Konfik)]` for easy setup 16 //! 17 //! ## Quick Start 18 //! 19 //! Add to your `Cargo.toml`: 20 //! 21 //! ```toml 22 //! [dependencies] 23 //! konfik = "0.1" 24 //! serde = { version = "1.0", features = ["derive"] } 25 //! clap = { version = "4.5", features = ["derive"] } # optional! only needed for cli arguments 26 //! ``` 27 //! 28 //! ### Basic Usage 29 //! 30 //! ```rust 31 //! use konfik::{ConfigLoader, LoadConfig, Konfik}; 32 //! use serde::Deserialize; 33 //! 34 //! #[derive(Deserialize, Konfik, Debug)] 35 //! struct AppConfig { 36 //! database_url: String, 37 //! port: u16, 38 //! debug: bool, 39 //! } 40 //! 41 //! fn main() -> Result<(), Box<dyn std::error::Error>> { 42 //! // Load with defaults (looks for config.json, config.yaml, config.toml) 43 //! let config = AppConfig::load()?; 44 //! 45 //! println!("Config: {:#?}", config); 46 //! Ok(()) 47 //! } 48 //! ``` 49 //! 50 //! ### Advanced Configuration 51 //! 52 //! ```rust 53 //! use konfik::{ConfigLoader, Error, Konfik}; 54 //! use serde::Deserialize; 55 //! use clap::Parser; 56 //! 57 //! #[derive(Deserialize, Konfik, Debug, Parser)] 58 //! struct AppConfig { 59 //! database_url: String, 60 //! port: u16, 61 //! debug: bool, 62 //! #[serde(skip)] 63 //! runtime_data: Option<String>, 64 //! } 65 //! 66 //! fn main() -> Result<(), Box<dyn std::error::Error>> { 67 //! let config = ConfigLoader::default() 68 //! .with_env_prefix("MYAPP") // Environment variables: MYAPP_DATABASE_URL, etc. 69 //! .with_config_file("app.toml") // Additional config file 70 //! .with_cli() // Enable CLI argument parsing 71 //! .with_validation(|config| { // Custom validation 72 //! if let Some(port) = config.get("port").and_then(|v| v.as_u64()) { 73 //! if port > 65535 { 74 //! return Err(Error::Validation("Port must be <= 65535".to_string())); 75 //! } 76 //! } 77 //! Ok(()) 78 //! }) 79 //! .load::<AppConfig>()?; 80 //! 81 //! println!("Loaded config: {:#?}", config); 82 //! Ok(()) 83 //! } 84 //! ``` 85 //! 86 //! ## Configuration Sources & Priority 87 //! 88 //! konfik loads configuration from multiple sources in the following priority order (higher priority overrides lower): 89 //! 90 //! 1. **CLI Arguments** (highest priority) 91 //! 2. **Environment Variables** 92 //! 3. **Configuration Files** (lowest priority) 93 //! 94 //! ### Configuration Files 95 //! 96 //! By default, konfik looks for these files in the current directory: 97 //! 98 //! - `config.json` 99 //! - `config.yaml` 100 //! - `config.toml` 101 //! 102 //! You can specify custom files: 103 //! 104 //! ```rust 105 //! let config = ConfigLoader::default() 106 //! .with_config_file("custom.toml") 107 //! .with_config_files(&["/etc/myapp/config.yaml", "config.json"]) 108 //! .load::<AppConfig>()?; 109 //! ``` 110 //! 111 //! ### Environment Variables 112 //! 113 //! Environment variables are automatically mapped from your struct fields: 114 //! 115 //! ```rust 116 //! #[derive(Deserialize, Konfik)] 117 //! struct Config { 118 //! database_url: String, // DATABASE_URL 119 //! api_key: String, // API_KEY 120 //! max_connections: u32, // MAX_CONNECTIONS 121 //! } 122 //! ``` 123 //! 124 //! With a prefix: 125 //! 126 //! ```rust 127 //! let config = ConfigLoader::default() 128 //! .with_env_prefix("MYAPP") // MYAPP_DATABASE_URL, MYAPP_API_KEY, etc. 129 //! .load::<Config>()?; 130 //! ``` 131 //! 132 //! ### CLI Arguments 133 //! 134 //! The CLI is integrated with `clap`. It detects at runtime which fields are still 135 //! missing and makes those required in the CLI: 136 //! 137 //! ```rust 138 //! #[derive(Deserialize, Konfik)] 139 //! struct Konfik { 140 //! database_url: String, // --database-url 141 //! max_connections: u32, // --max-connections 142 //! debug: bool, // --debug (flag, no value needed) 143 //! } 144 //! ``` 145 //! 146 //! ## Supported Types 147 //! 148 //! `Konfik` supports all types. 149 //! 150 //! ## Validation 151 //! 152 //! Add custom validation logic: 153 //! 154 //! ```rust 155 //! let config = ConfigLoader::default() 156 //! .with_validation(|config| { 157 //! // Validate port range 158 //! if let Some(port) = config.get("port").and_then(|v| v.as_u64()) { 159 //! if !(1024..=65535).contains(&port) { 160 //! return Err(Error::Validation("Port must be between 1024 and 65535".into())); 161 //! } 162 //! } 163 //! 164 //! // Validate required combinations 165 //! let has_ssl = config.get("ssl_enabled").and_then(|v| v.as_bool()).unwrap_or(false); 166 //! let has_ssl_cert = config.get("ssl_cert_path").and_then(|v| v.as_str()).is_some(); 167 //! 168 //! if has_ssl && !has_ssl_cert { 169 //! return Err(Error::Validation("SSL enabled but no certificate path provided".into())); 170 //! } 171 //! 172 //! Ok(()) 173 //! }) 174 //! .load::<AppConfig>()?; 175 //! ``` 176 177 mod config_loader; 178 pub mod config_meta; 179 mod error; 180 181 pub use config_loader::ConfigLoader; 182 pub use error::Error; 183 pub use konfik_derive::{Konfik, Nested}; 184 185 /// Simple trait for loading configuration 186 pub trait LoadConfig: Sized { 187 /// Load configuration from all available sources 188 /// 189 /// # Errors 190 /// 191 /// Both functions return an `Error` if any of the following occur: 192 /// 193 /// 1. **File I/O errors** – if reading configuration files fails (for `load_with`, this depends on the provided `ConfigLoader`). 194 /// 2. **Deserialization errors** – if converting the loaded configuration into `Self` fails. 195 /// 3. **Validation errors** – if any validation defined in the loader fails. 196 /// 4. **Other loader-specific errors** – any errors returned by the custom `ConfigLoader` in `load_with`. 197 fn load() -> Result<Self, Error>; 198 }