use serde::Deserialize; use std::path::Path; #[derive(Debug, Clone, Deserialize)] pub struct Config { pub gateway: GatewayConfig, pub storage: StorageConfig, #[serde(default)] pub catalog: CatalogConfig, #[serde(default)] pub query: QueryConfig, pub sidecar: SidecarConfig, #[serde(default)] pub ai: AiConfig, #[serde(default)] pub auth: AuthConfig, #[serde(default)] pub observability: ObservabilityConfig, } #[derive(Debug, Clone, Deserialize)] pub struct GatewayConfig { #[serde(default = "default_host")] pub host: String, #[serde(default = "default_gateway_port")] pub port: u16, } #[derive(Debug, Clone, Deserialize)] pub struct StorageConfig { #[serde(default = "default_storage_root")] pub root: String, } #[derive(Debug, Clone, Deserialize, Default)] pub struct CatalogConfig { #[serde(default = "default_manifest_prefix")] pub manifest_prefix: String, } #[derive(Debug, Clone, Deserialize, Default)] pub struct QueryConfig { pub max_rows_per_query: Option, } #[derive(Debug, Clone, Deserialize)] pub struct SidecarConfig { #[serde(default = "default_sidecar_url")] pub url: String, } #[derive(Debug, Clone, Deserialize, Default)] pub struct AiConfig { #[serde(default = "default_embed_model")] pub embed_model: String, #[serde(default = "default_gen_model")] pub gen_model: String, #[serde(default = "default_rerank_model")] pub rerank_model: String, } #[derive(Debug, Clone, Deserialize, Default)] pub struct AuthConfig { #[serde(default)] pub enabled: bool, pub api_key: Option, } #[derive(Debug, Clone, Deserialize, Default)] pub struct ObservabilityConfig { #[serde(default = "default_exporter")] pub exporter: String, #[serde(default = "default_service_name")] pub service_name: String, } // Defaults fn default_host() -> String { "0.0.0.0".to_string() } fn default_gateway_port() -> u16 { 3100 } fn default_storage_root() -> String { "./data".to_string() } fn default_manifest_prefix() -> String { "_catalog/manifests".to_string() } fn default_sidecar_url() -> String { "http://localhost:3200".to_string() } fn default_embed_model() -> String { "nomic-embed-text".to_string() } fn default_gen_model() -> String { "qwen2.5".to_string() } fn default_rerank_model() -> String { "qwen2.5".to_string() } fn default_exporter() -> String { "stdout".to_string() } fn default_service_name() -> String { "lakehouse".to_string() } impl Config { pub fn load(path: &str) -> Result { let path = Path::new(path); if !path.exists() { return Err(format!("config file not found: {}", path.display())); } let content = std::fs::read_to_string(path) .map_err(|e| format!("failed to read config: {e}"))?; toml::from_str(&content) .map_err(|e| format!("failed to parse config: {e}")) } pub fn load_or_default() -> Self { // Try lakehouse.toml in current dir, then /etc/lakehouse/lakehouse.toml for path in &["lakehouse.toml", "/etc/lakehouse/lakehouse.toml"] { if let Ok(config) = Self::load(path) { tracing::info!("loaded config from {path}"); return config; } } tracing::warn!("no config file found, using defaults"); Self::default() } } impl Default for Config { fn default() -> Self { Self { gateway: GatewayConfig { host: default_host(), port: default_gateway_port() }, storage: StorageConfig { root: default_storage_root() }, catalog: CatalogConfig::default(), query: QueryConfig::default(), sidecar: SidecarConfig { url: default_sidecar_url() }, ai: AiConfig::default(), auth: AuthConfig::default(), observability: ObservabilityConfig::default(), } } }