liminal-server 0.24.1

Standalone server for the liminal messaging bus
Documentation
//! Exact template data remains flat on disk with a closed decoding boundary.

use serde::{Deserialize, Serialize};
use std::path::PathBuf;

/// Immutable exact proposal; subjects come from the site, never tool arguments.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TemplateProposal {
    /// One template name, normally the site's name.
    pub name: String,
    /// Site this proposal was prepared against.
    pub service: String,
    /// Absolute canonical path approved by the person.
    pub command: PathBuf,
    /// Exact argument vector.
    pub args: Vec<String>,
    /// Site's exact subjects.
    pub subjects: Vec<String>,
    /// Only environment names, never values.
    pub env: Vec<String>,
    /// Positive termination deadline in milliseconds.
    pub stop_timeout_ms: u64,
    /// SHA-256 of command bytes at preparation, rechecked on approval.
    pub digest: String,
}

/// A live or retired record with all approvals kept.
#[derive(Clone, PartialEq, Eq, Serialize)]
pub struct TemplateRecord {
    /// Exact approved program and invocation.
    #[serde(flatten)]
    pub proposal: TemplateProposal,
    /// Actual people whose authenticated approvals authorized this record.
    pub approved_by: Vec<String>,
    /// Execution time of the approval in epoch milliseconds.
    pub approved_at_ms: u64,
    /// Retirement prevents every subsequent deployment and restart.
    pub retired: bool,
    /// All execution transitions with draft ids and word digests.
    pub transitions: Vec<super::super::site::Approval>,
}

impl<'de> Deserialize<'de> for TemplateRecord {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        #[derive(Deserialize)]
        #[serde(deny_unknown_fields)]
        struct Fields {
            name: String,
            service: String,
            command: PathBuf,
            args: Vec<String>,
            subjects: Vec<String>,
            env: Vec<String>,
            stop_timeout_ms: u64,
            digest: String,
            approved_by: Vec<String>,
            approved_at_ms: u64,
            retired: bool,
            transitions: Vec<super::super::site::Approval>,
        }
        let value = Fields::deserialize(deserializer)?;
        Ok(Self {
            proposal: TemplateProposal {
                name: value.name,
                service: value.service,
                command: value.command,
                args: value.args,
                subjects: value.subjects,
                env: value.env,
                stop_timeout_ms: value.stop_timeout_ms,
                digest: value.digest,
            },
            approved_by: value.approved_by,
            approved_at_ms: value.approved_at_ms,
            retired: value.retired,
            transitions: value.transitions,
        })
    }
}