use std::{net::SocketAddr, path::Path};
use anyhow::bail;
use config::FileFormat;
use dirs::config_dir;
use serde::{Deserialize, Serialize};
use crate::PROGRAM_NAME;
#[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,
}
#[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() -> anyhow::Result<Self> {
let file_name = format!("{PROGRAM_NAME}.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>()?)
}
}