use crate::{KreuzbergError, Result};
use serde::Deserialize;
use std::path::Path;
use super::ServerConfig;
pub fn from_file(path: impl AsRef<Path>) -> Result<ServerConfig> {
let path = path.as_ref();
let content = std::fs::read_to_string(path)
.map_err(|e| KreuzbergError::validation(format!("Failed to read config file {}: {}", path.display(), e)))?;
let extension = path.extension().and_then(|ext| ext.to_str()).ok_or_else(|| {
KreuzbergError::validation(format!(
"Cannot determine file format: no extension found in {}",
path.display()
))
})?;
let mut config = match extension.to_lowercase().as_str() {
"toml" => from_toml_str(&content, path)?,
"yaml" | "yml" => from_yaml_str(&content, path)?,
"json" => from_json_str(&content, path)?,
_ => {
return Err(KreuzbergError::validation(format!(
"Unsupported config file format: .{}. Supported formats: .toml, .yaml, .yml, .json",
extension
)));
}
};
config.normalize_legacy_fields();
Ok(config)
}
pub fn from_toml_file(path: impl AsRef<Path>) -> Result<ServerConfig> {
let path = path.as_ref();
let content = std::fs::read_to_string(path)
.map_err(|e| KreuzbergError::validation(format!("Failed to read config file {}: {}", path.display(), e)))?;
let mut config: ServerConfig = toml::from_str(&content)
.map_err(|e| KreuzbergError::validation(format!("Invalid TOML in {}: {}", path.display(), e)))?;
config.normalize_legacy_fields();
Ok(config)
}
pub fn from_yaml_file(path: impl AsRef<Path>) -> Result<ServerConfig> {
let path = path.as_ref();
let content = std::fs::read_to_string(path)
.map_err(|e| KreuzbergError::validation(format!("Failed to read config file {}: {}", path.display(), e)))?;
let mut config: ServerConfig = serde_yaml_ng::from_str(&content)
.map_err(|e| KreuzbergError::validation(format!("Invalid YAML in {}: {}", path.display(), e)))?;
config.normalize_legacy_fields();
Ok(config)
}
pub fn from_json_file(path: impl AsRef<Path>) -> Result<ServerConfig> {
let path = path.as_ref();
let content = std::fs::read_to_string(path)
.map_err(|e| KreuzbergError::validation(format!("Failed to read config file {}: {}", path.display(), e)))?;
let mut config: ServerConfig = serde_json::from_str(&content)
.map_err(|e| KreuzbergError::validation(format!("Invalid JSON in {}: {}", path.display(), e)))?;
config.normalize_legacy_fields();
Ok(config)
}
fn from_toml_str(content: &str, path: &Path) -> Result<ServerConfig> {
#[derive(Deserialize)]
struct RootConfig {
#[serde(default)]
server: Option<ServerConfig>,
}
if let Ok(root) = toml::from_str::<RootConfig>(content) {
if let Some(server) = root.server {
return Ok(server);
} else {
return toml::from_str::<ServerConfig>(content)
.map_err(|e| KreuzbergError::validation(format!("Invalid TOML in {}: {}", path.display(), e)));
}
}
toml::from_str::<ServerConfig>(content)
.map_err(|e| KreuzbergError::validation(format!("Invalid TOML in {}: {}", path.display(), e)))
}
fn from_yaml_str(content: &str, path: &Path) -> Result<ServerConfig> {
#[derive(Deserialize)]
struct RootConfig {
#[serde(default)]
server: Option<ServerConfig>,
}
if let Ok(root) = serde_yaml_ng::from_str::<RootConfig>(content) {
if let Some(server) = root.server {
return Ok(server);
} else {
return serde_yaml_ng::from_str::<ServerConfig>(content)
.map_err(|e| KreuzbergError::validation(format!("Invalid YAML in {}: {}", path.display(), e)));
}
}
serde_yaml_ng::from_str::<ServerConfig>(content)
.map_err(|e| KreuzbergError::validation(format!("Invalid YAML in {}: {}", path.display(), e)))
}
fn from_json_str(content: &str, path: &Path) -> Result<ServerConfig> {
#[derive(Deserialize)]
struct RootConfig {
#[serde(default)]
server: Option<ServerConfig>,
}
if let Ok(root) = serde_json::from_str::<RootConfig>(content) {
if let Some(server) = root.server {
return Ok(server);
} else {
return serde_json::from_str::<ServerConfig>(content)
.map_err(|e| KreuzbergError::validation(format!("Invalid JSON in {}: {}", path.display(), e)));
}
}
serde_json::from_str::<ServerConfig>(content)
.map_err(|e| KreuzbergError::validation(format!("Invalid JSON in {}: {}", path.display(), e)))
}