use std::path::PathBuf; use anyhow::{Result, bail}; /// Server configuration loaded from environment variables. #[derive(Debug, Clone)] pub struct Config { /// Bind address, e.g. "0.0.0.0:9432" pub bind: String, /// API key for Bearer auth pub api_key: String, /// Path to SQLite database file pub db_path: PathBuf, /// User-Agent header for OpenFood Facts API requests pub off_user_agent: String, } impl Config { pub fn from_env() -> Result { let bind = std::env::var("NUTRITION_MCP_BIND") .unwrap_or_else(|_| "0.0.0.0:9432".to_string()); let api_key = std::env::var("NUTRITION_MCP_API_KEY") .unwrap_or_else(|_| { tracing::warn!("NUTRITION_MCP_API_KEY not set — generating a random key"); uuid::Uuid::new_v4().to_string() }); let db_path = std::env::var("NUTRITION_MCP_DB_PATH") .map(PathBuf::from) .unwrap_or_else(|_| { let mut p = dirs_data_path(); p.push("nutrition"); p.push("nutrition.db"); p }); let off_user_agent = std::env::var("NUTRITION_MCP_OFF_USER_AGENT") .unwrap_or_else(|_| { "NutritionMCP/0.1.0 (personal use)".to_string() }); if api_key.is_empty() { bail!("NUTRITION_MCP_API_KEY must not be empty"); } Ok(Self { bind, api_key, db_path, off_user_agent, }) } /// Create the parent directory of db_path if it doesn't exist. pub fn ensure_db_dir(&self) -> Result<()> { if let Some(parent) = self.db_path.parent() { std::fs::create_dir_all(parent)?; } Ok(()) } } fn dirs_data_path() -> PathBuf { // Prefer XDG_DATA_HOME, fall back to ~/.local/share std::env::var("XDG_DATA_HOME") .map(PathBuf::from) .unwrap_or_else(|_| { let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string()); let mut p = PathBuf::from(home); p.push(".local"); p.push("share"); p }) }