use figment::{
Figment,
providers::{Env, Format, Toml},
};
use serde::Deserialize;
use std::net::SocketAddr;
use std::path::PathBuf;
use crate::error::Result;
pub const DEFAULT_BIND_ADDR: &str = "127.0.0.1:3000";
#[derive(Debug, Clone, Deserialize)]
#[non_exhaustive]
pub struct Config {
pub service_did: String,
pub service_endpoint: String,
#[serde(default = "default_bind_addr")]
pub bind_addr: SocketAddr,
pub db_path: PathBuf,
pub signing_key_path: PathBuf,
#[serde(default)]
pub admin: AdminConfigToml,
#[serde(default)]
pub labeler: Option<LabelerConfigToml>,
#[serde(default)]
pub operator: Option<OperatorConfigToml>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct AdminConfigToml {
#[serde(default)]
pub label_values: Option<Vec<String>>,
}
#[derive(Debug, Clone, serde::Serialize, Deserialize)]
pub struct LabelerConfigToml {
pub label_values: Vec<String>,
#[serde(default)]
pub label_value_definitions: Vec<LabelValueDefinitionToml>,
#[serde(default)]
pub reason_types: Vec<String>,
#[serde(default)]
pub subject_types: Vec<String>,
#[serde(default)]
pub subject_collections: Vec<String>,
}
#[derive(Debug, Clone, serde::Serialize, Deserialize)]
pub struct LabelValueDefinitionToml {
pub identifier: String,
pub severity: SeverityToml,
pub blurs: BlursToml,
#[serde(default)]
pub default_setting: Option<DefaultSettingToml>,
#[serde(default)]
pub adult_only: Option<bool>,
pub locales: Vec<LocaleToml>,
}
#[derive(Debug, Clone, Copy, serde::Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SeverityToml {
Inform,
Alert,
None,
}
#[derive(Debug, Clone, Copy, serde::Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum BlursToml {
Content,
Media,
None,
}
#[derive(Debug, Clone, Copy, serde::Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DefaultSettingToml {
Ignore,
Warn,
Hide,
}
#[derive(Debug, Clone, serde::Serialize, Deserialize)]
pub struct LocaleToml {
pub lang: String,
pub name: String,
pub description: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct OperatorConfigToml {
pub pds_url: String,
pub session_path: std::path::PathBuf,
}
fn default_bind_addr() -> SocketAddr {
DEFAULT_BIND_ADDR
.parse()
.expect("DEFAULT_BIND_ADDR is a valid socket address")
}
impl Config {
pub fn validate(&self) -> Result<()> {
url::Url::parse(&self.service_endpoint).map_err(|e| {
crate::error::Error::Signing(format!("config.service_endpoint is not a valid URL: {e}"))
})?;
Ok(())
}
pub fn load() -> Result<Self> {
let toml_path: PathBuf = std::env::var_os("CAIRN_CONFIG")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("/etc/cairn/cairn.toml"));
Self::load_from(Some(&toml_path))
}
pub fn load_from(toml_path: Option<&std::path::Path>) -> Result<Self> {
let mut fig = Figment::new();
if let Some(p) = toml_path
&& p.is_file()
{
fig = fig.merge(Toml::file(p));
}
fig = fig.merge(Env::prefixed("CAIRN_"));
let cfg: Config = fig.extract()?;
cfg.validate()?;
Ok(cfg)
}
}
impl From<AdminConfigToml> for crate::AdminConfig {
fn from(t: AdminConfigToml) -> Self {
crate::AdminConfig {
label_values: t.label_values,
}
}
}