righvalor 0.1.0

RighValor: AI Infrastructure and Applications Framework for the Far Edge
use std::path::PathBuf;

use anyhow::Result;
use clap::{Parser, Subcommand, ValueHint};
use serde::{Deserialize, Serialize};

/// Valor application configuration loaded at compile time from application.toml
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ValorApplicationConfig {
    cluster: ValorClusterConfig,
    master: ValorNodeConfig,
    worker: ValorNodeConfig,
    #[serde(default)]
    runtime: ValorRuntimeTuning,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ValorClusterConfig {
    pub cookie: String,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ValorNodeConfig {
    port: u16,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ValorRuntimeTuning {
    #[serde(default = "ValorRuntimeTuning::default_heartbeat_interval_ms")]
    pub heartbeat_interval_ms: u64,
    #[serde(default = "ValorRuntimeTuning::default_missed_before_unreachable")]
    pub missed_before_unreachable: u32,
    #[serde(default = "ValorRuntimeTuning::default_unreachable_after_secs")]
    pub unreachable_after_secs: u64,
    #[serde(default = "ValorRuntimeTuning::default_eviction_after_secs")]
    pub eviction_after_secs: u64,
    #[serde(default = "ValorRuntimeTuning::default_task_timeout_ms")]
    pub task_default_timeout_ms: u64,
}

impl Default for ValorRuntimeTuning {
    fn default() -> Self {
        Self {
            heartbeat_interval_ms: Self::default_heartbeat_interval_ms(),
            missed_before_unreachable: Self::default_missed_before_unreachable(),
            unreachable_after_secs: Self::default_unreachable_after_secs(),
            eviction_after_secs: Self::default_eviction_after_secs(),
            task_default_timeout_ms: Self::default_task_timeout_ms(),
        }
    }
}

impl ValorRuntimeTuning {
    fn default_heartbeat_interval_ms() -> u64 {
        5000
    }
    fn default_missed_before_unreachable() -> u32 {
        2
    }
    fn default_unreachable_after_secs() -> u64 {
        7
    }
    fn default_eviction_after_secs() -> u64 {
        30
    }
    fn default_task_timeout_ms() -> u64 {
        60_000
    }
}

impl ValorApplicationConfig {
    /// Load Valor configuration from embedded TOML at compile time
    pub fn load() -> Result<Self> {
        const CONFIG_CONTENT: &str = include_str!("../application.toml");
        let config: ValorApplicationConfig = toml::from_str(CONFIG_CONTENT)?;
        Ok(config)
    }

    /// Get Valor default hostname
    pub fn hostname(&self) -> String {
        "localhost".to_string()
    }

    pub fn cluster_cookie(&self) -> String {
        self.cluster.cookie.clone()
    }

    /// Get the Valor master port from config
    pub fn master_port(&self) -> u16 {
        self.master.port
    }

    /// Get the Valor worker port from config
    pub fn worker_port(&self) -> u16 {
        self.worker.port
    }

    pub fn tuning(&self) -> &ValorRuntimeTuning {
        &self.runtime
    }
}

/// Valor CLI configuration and commands
#[derive(Parser, Debug, Clone)]
#[command(name = "righvalor")]
#[command(about = "RighValor AI Infrastructure for the Far Edge")]
#[command(version)]
pub struct ValorCli {
    /// Node ID/name for this valor instance
    #[arg(long)]
    pub id: String,

    #[command(subcommand)]
    pub command: ValorCommand,

    /// File path with certificate for private key
    #[arg(
        long,
        value_hint = ValueHint::FilePath,
        help = "Path to the CA certificate file."
    )]
    pub ca_cert: Option<String>,

    /// File path with peer CA certificate
    #[arg(
        long,
        value_hint = ValueHint::FilePath,
        help = "Path to the peer certificate file."
    )]
    pub certificate: Option<String>,

    /// File path with private key
    #[arg(
        long,
        value_hint = ValueHint::FilePath,
        help = "Path to the private key file."
    )]
    pub private_key: Option<String>,

    /// UDS socket path for runtime configuration
    #[arg(long, default_value = "/tmp/righvalor.sock")]
    pub uds: PathBuf,

    /// Service registry config file (TOML). If omitted, worker uses default.
    #[arg(long, value_hint = ValueHint::FilePath)]
    pub service_toml: Option<PathBuf>,
}

#[derive(Subcommand, Debug, Clone)]
pub enum ValorCommand {
    /// Start as Valor Master node
    Master,
    /// Start as Valor Worker node
    Worker,
    /// Register a worker with master via UDS
    RegisterWorker {
        /// Worker ID to register
        #[arg(long)]
        worker_id: String,
        /// Worker IP address
        #[arg(long)]
        worker_ip: String,
    },
}

/// Combined Valor configuration holding both CLI and application configs
#[derive(Debug, Clone)]
pub struct ValorConfig {
    pub cli: ValorCli,
    pub app: ValorApplicationConfig,
}

impl ValorConfig {
    /// Create a new Valor configuration by parsing CLI and loading compile-time TOML
    pub fn new() -> Result<Self> {
        let cli = ValorCli::parse();
        let app = ValorApplicationConfig::load()?;

        Ok(ValorConfig { cli, app })
    }
}