iridium-db 0.2.0

A high-performance vector-graph hybrid storage and indexing engine
use std::path::PathBuf;

use alloy_deploy_profile::api as deploy_profile_api;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ServiceTlsMode {
    Disabled,
    OperatorOptional,
}

impl ServiceTlsMode {
    pub fn parse(value: &str) -> Option<Self> {
        match value {
            "disabled" => Some(Self::Disabled),
            "operator-optional" => Some(Self::OperatorOptional),
            _ => None,
        }
    }

    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Disabled => "disabled",
            Self::OperatorOptional => "operator-optional",
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ServiceConfig {
    pub listen_address: String,
    pub data_dir: PathBuf,
    pub telemetry_endpoint: String,
    pub tls_mode: ServiceTlsMode,
    pub admin_token: Option<String>,
    pub max_requests: usize,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ServiceValidationReport {
    pub profile_id: &'static str,
    pub compatible: bool,
    pub missing_env: Vec<&'static str>,
    pub missing_verbs: Vec<&'static str>,
    pub missing_evidence_artifacts: Vec<&'static str>,
    pub warnings: Vec<String>,
}

#[derive(Debug)]
pub enum ServiceError {
    InvalidConfig(String),
    Io(std::io::Error),
    Driver(crate::features::client::DriverError),
}

impl From<std::io::Error> for ServiceError {
    fn from(err: std::io::Error) -> Self {
        Self::Io(err)
    }
}

impl From<crate::features::client::DriverError> for ServiceError {
    fn from(err: crate::features::client::DriverError) -> Self {
        Self::Driver(err)
    }
}

impl ServiceValidationReport {
    pub fn to_json_pretty(&self) -> String {
        let missing_env = self
            .missing_env
            .iter()
            .map(|entry| format!("\"{}\"", entry))
            .collect::<Vec<_>>()
            .join(", ");
        let missing_verbs = self
            .missing_verbs
            .iter()
            .map(|entry| format!("\"{}\"", entry))
            .collect::<Vec<_>>()
            .join(", ");
        let missing_evidence_artifacts = self
            .missing_evidence_artifacts
            .iter()
            .map(|entry| format!("\"{}\"", entry))
            .collect::<Vec<_>>()
            .join(", ");
        let warnings = self
            .warnings
            .iter()
            .map(|entry| format!("\"{}\"", entry.replace('"', "\\\"")))
            .collect::<Vec<_>>()
            .join(", ");

        format!(
            concat!(
                "{{\n",
                "  \"profile_id\": \"{}\",\n",
                "  \"compatible\": {},\n",
                "  \"missing_env\": [{}],\n",
                "  \"missing_verbs\": [{}],\n",
                "  \"missing_evidence_artifacts\": [{}],\n",
                "  \"warnings\": [{}]\n",
                "}}\n"
            ),
            self.profile_id,
            self.compatible,
            missing_env,
            missing_verbs,
            missing_evidence_artifacts,
            warnings
        )
    }
}

impl ServiceConfig {
    pub fn validate(&self) -> ServiceValidationReport {
        let profile = deploy_profile_api::service_candidate_profile();
        let available_env = ["listen_address", "data_dir", "telemetry_endpoint"];
        let supported_verbs = ["inspect", "validate", "report", "start", "stop", "status"];
        let available_evidence_artifacts = ["benchmark-report", "evidence-report"];
        let missing_env = missing_required(profile.required_env, &available_env);
        let missing_verbs = missing_required(profile.required_verbs, &supported_verbs);
        let missing_evidence_artifacts = missing_required(
            profile.required_evidence_artifacts,
            &available_evidence_artifacts,
        );

        let mut warnings = Vec::new();
        if matches!(self.tls_mode, ServiceTlsMode::Disabled) {
            warnings.push(
                "tls_mode=disabled leaves the Sprint 4 surface suitable only for trusted environments"
                    .to_string(),
            );
        }
        if self.admin_token.is_none() {
            warnings.push(
                "admin_token is unset; admin routes are exposed without operator authentication"
                    .to_string(),
            );
        }
        if self.max_requests == 0 {
            warnings.push("max_requests=0 enables an unbounded serve loop".to_string());
        }

        ServiceValidationReport {
            profile_id: profile.profile_id,
            compatible: missing_env.is_empty()
                && missing_verbs.is_empty()
                && missing_evidence_artifacts.is_empty(),
            missing_env,
            missing_verbs,
            missing_evidence_artifacts,
            warnings,
        }
    }
}

fn missing_required(required: &[&'static str], available: &[&str]) -> Vec<&'static str> {
    required
        .iter()
        .copied()
        .filter(|entry| !available.contains(entry))
        .collect()
}