use std::{net::SocketAddr, path::Path};
use anyhow::bail;
use config::FileFormat;
use dirs::config_dir;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Settings {
pub oparl: OParlSettings,
pub http: HttpSettings,
pub api: Option<ApiSettings>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct OParlSettings {
pub base_url: oparl_types::Url,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HttpSettings {
pub bind: Vec<SocketAddr>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApiSettings {
pub oidc: ApiOidcSettings,
pub http: HttpSettings,
#[serde(default)]
pub hide_product_signature: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApiOidcSettings {
pub issuer: String,
pub client_id: String,
pub client_secret: String,
}
impl Settings {
pub fn load(program_name: &str, suffix: Option<&str>) -> anyhow::Result<Self> {
let suffix = suffix.unwrap_or_default();
let file_name = format!("{program_name}{suffix}.toml");
let config_paths = vec![
config_dir().unwrap().join(program_name).join(&file_name),
Path::new("/etc").join(program_name).join(&file_name),
];
for config_path in &config_paths {
if config_path.exists() {
return Self::load_from_file(config_path);
}
}
bail!(
"Couldn't find a valid configuration file, searched in these paths: {config_paths:?}"
);
}
fn load_from_file(path: &Path) -> anyhow::Result<Self> {
log::info!("Loading configuration file {path:?}");
let config = config::Config::builder()
.add_source(
config::File::from(path)
.required(true)
.format(FileFormat::Toml),
)
.build()?;
Ok(config.try_deserialize::<Settings>()?)
}
}