Skip to main content

liminal_server/services/site/
lifecycle_records.rs

1//! Service shapes migrate absent construction fields on read.
2
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5
6/// Where a service stands.
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum ServiceState {
10    /// Registered, not running: created, or exited without being asked.
11    Asleep,
12    /// Contracts are approved; construction has not completed.
13    Building,
14    /// Its worker registered its subjects.
15    Live,
16    /// Stopped by a holder; woken by one.
17    Held,
18    /// Stopped for good; kept with its history.
19    Retired,
20}
21
22/// One change of state, with who made it and when.
23#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub struct Transition {
25    /// The state before.
26    pub from: ServiceState,
27    /// The state after.
28    pub to: ServiceState,
29    /// The Lys holder that asked, or `process` for the service's own exit.
30    pub holder: String,
31    /// Milliseconds since the epoch.
32    pub at_ms: u64,
33    /// What happened beside the state, such as an exit status.
34    #[serde(default, skip_serializing_if = "Option::is_none")]
35    pub note: Option<String>,
36}
37
38/// One service, as kept.
39#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
40pub struct ServiceRecord {
41    /// Its id, which is also its `liminal.service` id in Lys.
42    pub id: String,
43    /// The name it was created with, one segment.
44    pub name: String,
45    /// The template it runs.
46    #[serde(default)]
47    pub template: Option<String>,
48    /// Exact serving subjects. Configuration creates each root with the name;
49    /// construction keeps its promised subject identifiers unchanged.
50    pub subjects: Vec<String>,
51    /// Approved purpose, absent on records migrated from configuration services.
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub purpose: Option<String>,
54    /// Exact input/output promises, empty on pre-construction records.
55    #[serde(default)]
56    pub contracts: BTreeMap<String, super::site::Contract>,
57    /// The sole builder, kept after completion for attribution.
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    pub builder: Option<String>,
60    /// Grant ids retained even after revocation for the audit history.
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub builder_grants: Option<super::site::BuilderGrants>,
63    /// Whether the binding still admits construction writes.
64    #[serde(default)]
65    pub builder_bound: bool,
66    /// Immutable actual site approvers from the approval connector, or `lys` for an act Lys
67    /// answered outright.
68    #[serde(default)]
69    pub approved_by: Vec<String>,
70    /// The person answerable for the site: the responsible person of the pass that created it.
71    #[serde(default, skip_serializing_if = "Option::is_none")]
72    pub responsible: Option<String>,
73    /// Retained plan and its complete write history.
74    #[serde(default)]
75    pub plan: super::plan::Plan,
76    /// Approval receipts retained alongside this service's transitions.
77    #[serde(default)]
78    pub approvals: Vec<super::site::Approval>,
79    /// Where it stands.
80    pub state: ServiceState,
81    /// The holder that created it.
82    pub created_by: String,
83    /// Every transition, oldest first.
84    pub transitions: Vec<Transition>,
85}
86
87impl std::fmt::Debug for ServiceRecord {
88    fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        out.debug_struct("ServiceRecord")
90            .field("state", &self.state)
91            .field("transitions", &self.transitions.len())
92            .finish_non_exhaustive()
93    }
94}