nautalid 0.1.0

Scratch container substrate — TLS 1.3 HTTP/2+3 kernel, LID/AetherDB, optional filter bus (GPL-3.0-or-later).
//! AetherDB deployment topology — replication vs horizontal cluster (sharded RAM).

/// How Nautalid opens AetherDB across machines.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeployMode {
    /// In-process embedded (`local`).
    Local,
    /// Full dataset replicated on each wire peer (`shards` usually 1).
    Replicate,
    /// Dataset partitioned across logical shards (`shards` > 1, cluster control plane).
    Cluster,
}

/// Shard/replication/query layout shared by wire clients, hub, and data nodes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Topology {
    pub mode: DeployMode,
    pub shards: u32,
    pub replication: u32,
    pub node_id: u64,
    pub query: String,
}

impl Topology {
  #[must_use]
    pub fn from_env() -> Self {
        let mode = deploy_mode_from_env();
        Self {
            mode,
            shards: shards_from_env(mode),
            replication: replication_from_env(),
            node_id: node_id_from_env(),
            query: query_from_env(mode),
        }
    }

    /// Defaults for embedded single-process open.
    #[must_use]
    pub fn local_default() -> Self {
        Self {
            mode: DeployMode::Local,
            shards: 1,
            replication: 1,
            node_id: 1,
            query: "kv".into(),
        }
    }

    #[must_use]
    pub fn is_cluster(&self) -> bool {
        self.mode == DeployMode::Cluster
    }

    #[must_use]
    pub fn bootstrap_leader(&self) -> bool {
        self.is_cluster() && self.node_id == 1
    }

    #[must_use]
    pub fn mode_label(&self) -> &'static str {
        match self.mode {
            DeployMode::Local => "local",
            DeployMode::Replicate => "replicate",
            DeployMode::Cluster => "cluster",
        }
    }
}

pub fn deploy_mode_from_env() -> DeployMode {
    match std::env::var("NAUTALID_AETHERDB_MODE")
        .ok()
        .as_deref()
        .map(str::trim)
        .map(str::to_ascii_lowercase)
        .as_deref()
    {
        Some("cluster") => DeployMode::Cluster,
        Some("replicate") | Some("replay") => DeployMode::Replicate,
        Some("local") | Some("embedded") => DeployMode::Local,
        _ => DeployMode::Replicate,
    }
}

pub fn host_mode_from_env() -> DeployMode {
    match std::env::var("AETHERDB_HOST_MODE")
        .ok()
        .as_deref()
        .map(str::trim)
        .map(str::to_ascii_lowercase)
        .as_deref()
    {
        Some("cluster") => DeployMode::Cluster,
        Some("replicate") | Some("replay") => DeployMode::Replicate,
        _ => deploy_mode_from_env(),
    }
}

fn shards_from_env(mode: DeployMode) -> u32 {
    std::env::var("NAUTALID_AETHERDB_SHARDS")
        .ok()
        .or_else(|| std::env::var("AETHERDB_HOST_SHARDS").ok())
        .and_then(|s| s.parse().ok())
        .unwrap_or(match mode {
            DeployMode::Cluster => 16,
            DeployMode::Replicate | DeployMode::Local => 1,
        })
        .max(1)
}

fn replication_from_env() -> u32 {
    std::env::var("NAUTALID_AETHERDB_REPLICATION")
        .ok()
        .or_else(|| std::env::var("AETHERDB_HOST_REPLICATION").ok())
        .and_then(|s| s.parse().ok())
        .unwrap_or(3)
        .max(1)
}

fn node_id_from_env() -> u64 {
    std::env::var("NAUTALID_AETHERDB_NODE_ID")
        .ok()
        .or_else(|| std::env::var("AETHERDB_HOST_NODE_ID").ok())
        .and_then(|s| s.parse().ok())
        .unwrap_or(1)
        .max(1)
}

fn query_from_env(mode: DeployMode) -> String {
    std::env::var("NAUTALID_AETHERDB_QUERY")
        .ok()
        .or_else(|| std::env::var("AETHERDB_HOST_QUERY").ok())
        .filter(|s| !s.trim().is_empty())
        .unwrap_or_else(|| match mode {
            DeployMode::Cluster => "sql".into(),
            DeployMode::Replicate | DeployMode::Local => "kv".into(),
        })
}

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

    #[test]
    fn cluster_defaults_sixteen_shards() {
        unsafe {
            std::env::set_var("NAUTALID_AETHERDB_MODE", "cluster");
            std::env::remove_var("NAUTALID_AETHERDB_SHARDS");
        }
        let topo = Topology::from_env();
        assert_eq!(topo.mode, DeployMode::Cluster);
        assert_eq!(topo.shards, 16);
        assert_eq!(topo.query, "sql");
        unsafe {
            std::env::remove_var("NAUTALID_AETHERDB_MODE");
        }
    }

    #[test]
    fn replicate_defaults_one_shard() {
        unsafe {
            std::env::set_var("NAUTALID_AETHERDB_MODE", "replicate");
            std::env::remove_var("NAUTALID_AETHERDB_SHARDS");
        }
        let topo = Topology::from_env();
        assert_eq!(topo.shards, 1);
        unsafe {
            std::env::remove_var("NAUTALID_AETHERDB_MODE");
        }
    }
}