iocaine 2.2.0

The deadliest poison known to AI
Documentation
// SPDX-FileCopyrightText: 2025 Gergely Nagy
// SPDX-FileContributor: Gergely Nagy
//
// SPDX-License-Identifier: MIT

mod generator;
mod metrics;
mod request_handler;
mod server;
mod server_control;
mod source;
mod templates;

pub use generator::GeneratorConfig;
pub use metrics::{MetricsConfig, MetricsLabel};
pub use request_handler::RequestHandlerConfig;
pub use server::ServerConfig;
pub use source::SourceConfig;
pub use templates::{TemplatesConfig, TemplatesMinifyConfig};

use anyhow::anyhow;
use figment::{
    Figment,
    providers::{Env, Format, Toml},
};
use figment_file_provider_adapter::FileAdapter;
use serde::Deserialize;

#[derive(Clone, Debug, Deserialize)]
pub struct Config {
    #[serde(default)]
    pub server: server::ServerConfig,
    pub sources: source::SourceConfig,
    #[serde(default)]
    pub generator: generator::GeneratorConfig,
    #[serde(default)]
    pub templates: templates::TemplatesConfig,
    #[serde(default)]
    pub metrics: metrics::MetricsConfig,
}

impl Config {
    pub fn load(path: &str) -> Result<Self, anyhow::Error> {
        let config = Figment::new()
            .merge(FileAdapter::wrap(Env::prefixed("IOCAINE_").split("__")))
            .merge(FileAdapter::wrap(Env::prefixed("IOCAINE__").split("__")))
            .merge(FileAdapter::wrap(Toml::file(path)))
            .extract();

        let mut config: Self = match config {
            Ok(v) => v,
            Err(e) => {
                return Err(anyhow!(
                    "Failed to load configuration from {path} (and environment variables): {e}"
                ));
            }
        };

        if config.metrics.enable
            && config
                .metrics
                .labels
                .contains(&MetricsLabel::UserAgentGroup)
            && config.metrics.agent_group.is_empty()
        {
            tracing::warn!(
                "UserAgentGroup metrics labels are enabled, but no grouping has been configured. Disabling the label."
            );
            config
                .metrics
                .labels
                .retain(|label| label != &MetricsLabel::UserAgentGroup);
        }

        Ok(config)
    }

    #[must_use]
    pub fn is_compatible(&self, other: &Self) -> bool {
        if self.server.bind != other.server.bind
            || self.server.unix_listen_access != other.server.unix_listen_access
        {
            return false;
        }

        if self.metrics.enable != other.metrics.enable
            || self.metrics.labels != other.metrics.labels
            || self.metrics.bind != other.metrics.bind
            || self.metrics.agent_group != other.metrics.agent_group
        {
            return false;
        }

        true
    }
}