use crate::config::Config;
use crate::error::ConfigError;
pub trait FromConfig: Sized {
fn from_config(config: &Config) -> Result<Self, ConfigError>;
}
pub use klauthed_macros::FromConfig;
#[cfg(test)]
mod tests {
use super::*;
use crate::config::provider::MemoryProvider;
use crate::config::{ConfigBuilder, Profile};
use crate::error::ConfigError;
use serde::Deserialize;
use serde_json::json;
#[derive(Debug, PartialEq, Eq, Deserialize, FromConfig)]
#[config(key = "database")]
struct Database {
host: String,
port: u16,
}
#[derive(Debug, PartialEq, Eq, Deserialize, FromConfig)]
struct ServerSettings {
workers: u32,
}
#[derive(Debug, Default, PartialEq, Eq, Deserialize, FromConfig)]
#[config(key = "cache", default)]
struct Cache {
ttl_secs: u64,
}
async fn config_with(key: &str, value: serde_json::Value) -> Config {
ConfigBuilder::new(Profile::Test)
.with_provider(MemoryProvider::new().set(key, value))
.build()
.await
.expect("build config")
}
#[tokio::test]
async fn binds_explicit_key() {
let config = config_with("database", json!({ "host": "db", "port": 5432 })).await;
assert_eq!(
Database::from_config(&config).unwrap(),
Database { host: "db".to_owned(), port: 5432 }
);
}
#[tokio::test]
async fn defaults_key_to_snake_cased_type_name() {
let config = config_with("server_settings", json!({ "workers": 8 })).await;
assert_eq!(ServerSettings::from_config(&config).unwrap(), ServerSettings { workers: 8 });
}
#[tokio::test]
async fn default_mode_binds_missing_section_to_default() {
let config = config_with("unrelated", json!(true)).await;
assert_eq!(Cache::from_config(&config).unwrap(), Cache { ttl_secs: 0 });
}
#[tokio::test]
async fn missing_required_section_errors() {
let config = config_with("unrelated", json!(true)).await;
assert!(matches!(Database::from_config(&config), Err(ConfigError::MissingRequired(_))));
}
}