liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
//! Service shapes migrate absent construction fields on read.

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

/// Where a service stands.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ServiceState {
    /// Registered, not running: created, or exited without being asked.
    Asleep,
    /// Contracts are approved; construction has not completed.
    Building,
    /// Its worker registered its subjects.
    Live,
    /// Stopped by a holder; woken by one.
    Held,
    /// Stopped for good; kept with its history.
    Retired,
}

/// One change of state, with who made it and when.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Transition {
    /// The state before.
    pub from: ServiceState,
    /// The state after.
    pub to: ServiceState,
    /// The Lys holder that asked, or `process` for the service's own exit.
    pub holder: String,
    /// Milliseconds since the epoch.
    pub at_ms: u64,
    /// What happened beside the state, such as an exit status.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub note: Option<String>,
}

/// One service, as kept.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ServiceRecord {
    /// Its id, which is also its `liminal.service` id in Lys.
    pub id: String,
    /// The name it was created with, one segment.
    pub name: String,
    /// The template it runs.
    #[serde(default)]
    pub template: Option<String>,
    /// Exact serving subjects. Configuration creates each root with the name;
    /// construction keeps its promised subject identifiers unchanged.
    pub subjects: Vec<String>,
    /// Approved purpose, absent on records migrated from configuration services.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub purpose: Option<String>,
    /// Exact input/output promises, empty on pre-construction records.
    #[serde(default)]
    pub contracts: BTreeMap<String, super::site::Contract>,
    /// The sole builder, kept after completion for attribution.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub builder: Option<String>,
    /// Grant ids retained even after revocation for the audit history.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub builder_grants: Option<super::site::BuilderGrants>,
    /// Whether the binding still admits construction writes.
    #[serde(default)]
    pub builder_bound: bool,
    /// Immutable actual site approvers from the approval connector, or `lys` for an act Lys
    /// answered outright.
    #[serde(default)]
    pub approved_by: Vec<String>,
    /// The person answerable for the site: the responsible person of the pass that created it.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub responsible: Option<String>,
    /// Retained plan and its complete write history.
    #[serde(default)]
    pub plan: super::plan::Plan,
    /// Approval receipts retained alongside this service's transitions.
    #[serde(default)]
    pub approvals: Vec<super::site::Approval>,
    /// Where it stands.
    pub state: ServiceState,
    /// The holder that created it.
    pub created_by: String,
    /// Every transition, oldest first.
    pub transitions: Vec<Transition>,
}

impl std::fmt::Debug for ServiceRecord {
    fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        out.debug_struct("ServiceRecord")
            .field("state", &self.state)
            .field("transitions", &self.transitions.len())
            .finish_non_exhaustive()
    }
}