liminal-server 0.23.1

Standalone server for the liminal messaging bus
Documentation
//! The topology document (SRV-011 R1), built from in-memory state only.

use liminal::channel::ChannelMode;

use crate::ServerError;
use crate::server::connection::channel_registry::{ChannelOrigin, ChannelStatus};
use crate::server::connection::services::LiminalConnectionServices;

/// One channel (a line) as the console draws it.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChannelNode {
    /// The channel name.
    pub name: String,
    /// `durable` or `ephemeral`.
    pub mode: &'static str,
    /// `live` (admitting) or `held` (quiesced).
    pub state: &'static str,
    /// `config` (boot) or `runtime` (registered live).
    pub origin: &'static str,
    /// The quiesce cause, when held.
    pub reason: Option<String>,
}

/// The topology document.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TopologySnapshot {
    /// Every channel on the roster, sorted by name.
    pub channels: Vec<ChannelNode>,
    /// Every registered conversation responder subject, sorted.
    pub services: Vec<String>,
}

/// Builds the topology from the roster, read once (pinned by
/// `the_snapshot_reads_the_roster_once`), and the responder registry. Reads
/// no store row.
///
/// # Errors
/// Returns [`ServerError`] when the roster or responder lock is poisoned.
pub fn snapshot(services: &LiminalConnectionServices) -> Result<TopologySnapshot, ServerError> {
    let roster_error = |error: &dyn std::fmt::Display| ServerError::ListenerAccept {
        message: format!("console topology: {error}"),
    };
    let census = services
        .channel_census()
        .map_err(|error| roster_error(&error))?;
    let mut channels = Vec::with_capacity(census.len());
    for (name, status) in census {
        let node = match status {
            ChannelStatus::NotRegistered => continue,
            ChannelStatus::Active { origin, mode, .. } => ChannelNode {
                name,
                mode: mode_word(mode),
                state: "live",
                origin: origin_word(origin),
                reason: None,
            },
            ChannelStatus::Quiesced {
                reason,
                origin,
                mode,
            } => ChannelNode {
                name,
                mode: mode_word(mode),
                state: "held",
                origin: origin_word(origin),
                reason: Some(reason),
            },
        };
        channels.push(node);
    }
    Ok(TopologySnapshot {
        channels,
        services: services.responder_subjects()?,
    })
}

const fn mode_word(mode: ChannelMode) -> &'static str {
    match mode {
        ChannelMode::Durable => "durable",
        ChannelMode::Ephemeral => "ephemeral",
    }
}

const fn origin_word(origin: ChannelOrigin) -> &'static str {
    match origin {
        ChannelOrigin::BootConfigured => "config",
        ChannelOrigin::RuntimeRegistered => "runtime",
    }
}