audb-cli 0.1.11

Command-line interface for AuDB database application framework
//! Deployment configuration parsing
//!
//! This module defines the types and parsing logic for deployment configuration
//! from gold files (config deployment blocks).

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Deployment configuration from gold files
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeploymentConfig {
    /// Deployment target (docker, systemd, daemon, local)
    pub target: DeploymentTarget,

    /// Whether to persist across reboots
    #[serde(default = "default_persist")]
    pub persist: bool,

    /// Port to expose (for server applications)
    pub port: Option<u16>,

    /// Environment variables
    #[serde(default)]
    pub environment: HashMap<String, String>,

    /// Volume mappings (source -> destination)
    #[serde(default)]
    pub volumes: HashMap<String, String>,

    /// Health check configuration
    pub healthcheck: Option<HealthCheckConfig>,

    /// Restart policy (always, on-failure, unless-stopped)
    #[serde(default = "default_restart")]
    pub restart: String,
}

/// Deployment target type
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DeploymentTarget {
    /// Docker container deployment
    Docker,
    /// Systemd service (Linux)
    Systemd,
    /// Background daemon process
    Daemon,
    /// Local foreground process
    Local,
}

impl DeploymentTarget {
    pub fn as_str(&self) -> &str {
        match self {
            DeploymentTarget::Docker => "docker",
            DeploymentTarget::Systemd => "systemd",
            DeploymentTarget::Daemon => "daemon",
            DeploymentTarget::Local => "local",
        }
    }

    pub fn from_str(s: &str) -> Option<Self> {
        match s.to_lowercase().as_str() {
            "docker" => Some(DeploymentTarget::Docker),
            "systemd" => Some(DeploymentTarget::Systemd),
            "daemon" => Some(DeploymentTarget::Daemon),
            "local" => Some(DeploymentTarget::Local),
            _ => None,
        }
    }
}

/// Health check configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthCheckConfig {
    /// Health check endpoint (e.g., "/health")
    pub endpoint: String,

    /// Check interval (e.g., "30s")
    #[serde(default = "default_interval")]
    pub interval: String,

    /// Timeout for health check (e.g., "60s")
    #[serde(default = "default_timeout")]
    pub timeout: String,

    /// Number of retries before marking unhealthy
    #[serde(default = "default_retries")]
    pub retries: u32,
}

impl Default for HealthCheckConfig {
    fn default() -> Self {
        Self {
            endpoint: "/health".to_string(),
            interval: "5s".to_string(),
            timeout: "60s".to_string(),
            retries: 3,
        }
    }
}

impl Default for DeploymentConfig {
    fn default() -> Self {
        Self {
            target: DeploymentTarget::Docker,
            persist: true,
            port: Some(8080),
            environment: HashMap::new(),
            volumes: HashMap::new(),
            healthcheck: None,
            restart: "always".to_string(),
        }
    }
}

fn default_persist() -> bool {
    true
}

fn default_restart() -> String {
    "always".to_string()
}

fn default_interval() -> String {
    "30s".to_string()
}

fn default_timeout() -> String {
    "60s".to_string()
}

fn default_retries() -> u32 {
    3
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_deployment_target_from_str() {
        assert_eq!(
            DeploymentTarget::from_str("docker"),
            Some(DeploymentTarget::Docker)
        );
        assert_eq!(
            DeploymentTarget::from_str("systemd"),
            Some(DeploymentTarget::Systemd)
        );
        assert_eq!(
            DeploymentTarget::from_str("DOCKER"),
            Some(DeploymentTarget::Docker)
        );
        assert_eq!(DeploymentTarget::from_str("unknown"), None);
    }

    #[test]
    fn test_deployment_config_default() {
        let config = DeploymentConfig::default();
        assert_eq!(config.target, DeploymentTarget::Docker);
        assert!(config.persist);
        assert_eq!(config.port, Some(8080));
        assert_eq!(config.restart, "always");
    }
}