Skip to main content

liminal_server/auth_lys/
drafts_target.rs

1//! The one approved-draft store, dispatching to the registry that owns the target.
2
3use super::{
4    AgentDraftStore, ApprovalEvidence, ApprovalTransition, AtomicDraftStore, DraftError,
5    issuer_approval,
6};
7use lys_pass::drafts::{ApprovedDraft, Execution};
8use std::num::NonZeroUsize;
9
10/// Dispatches through the owning atomic registry, sharing no unapproved command.
11pub struct TargetDraftStore {
12    pub agents: AgentDraftStore,
13    pub sites: crate::services::lifecycle::site::executor::SiteDraftStore,
14}
15impl TargetDraftStore {
16    fn target(&mut self, draft: &ApprovedDraft) -> Result<&mut dyn AtomicDraftStore, DraftError> {
17        match draft.target.kind.as_str() {
18            "liminal.agent" => Ok(&mut self.agents),
19            "liminal.network" if draft.target.action == "create_site" => Ok(&mut self.sites),
20            "liminal.service" | "liminal.template" => Ok(&mut self.sites),
21            _ => Err(DraftError::Contract),
22        }
23    }
24}
25impl AtomicDraftStore for TargetDraftStore {
26    fn receipt(&self, id: &str) -> Result<Option<Execution>, DraftError> {
27        let agent = self.agents.receipt(id)?;
28        let site = self.sites.receipt(id)?;
29        if agent.is_some() && site.is_some() {
30            return Err(DraftError::ReceiptMismatch);
31        }
32        Ok(agent.or(site))
33    }
34    fn approval(&self, draft: &ApprovedDraft) -> Result<ApprovalEvidence, DraftError> {
35        issuer_approval(&self.agents.journal, draft)
36    }
37    fn commit(
38        &mut self,
39        draft: &ApprovedDraft,
40        approval: &ApprovalTransition,
41    ) -> Result<Execution, DraftError> {
42        let bound = self.agents.journal.read(|snapshot| {
43            snapshot
44                .prepared
45                .values()
46                .any(|prepared| prepared.draft.as_deref() == Some(&draft.id))
47        })?;
48        if !bound {
49            return self.agents.commit(draft, approval);
50        }
51        self.target(draft)?.commit(draft, approval)
52    }
53    fn acknowledged(&self, id: &str) -> Result<bool, DraftError> {
54        if self.receipt_owner(id)? {
55            self.agents.acknowledged(id)
56        } else {
57            self.sites.acknowledged(id)
58        }
59    }
60    fn acknowledge(&mut self, id: &str, receipt: &Execution) -> Result<(), DraftError> {
61        if self.receipt_owner(id)? {
62            self.agents.acknowledge(id, receipt)
63        } else {
64            self.sites.acknowledge(id, receipt)
65        }
66    }
67    fn pending_acknowledgments(
68        &self,
69        limit: NonZeroUsize,
70    ) -> Result<Vec<ApprovedDraft>, DraftError> {
71        let mut pending = self.agents.pending_acknowledgments(limit)?;
72        pending.extend(self.sites.pending_acknowledgments(limit)?);
73        if pending.len() > limit.get() {
74            return Err(DraftError::Bound);
75        }
76        Ok(pending)
77    }
78    fn deliver_outcome(
79        &mut self,
80        draft: &ApprovedDraft,
81        receipt: &Execution,
82    ) -> Result<(), DraftError> {
83        if self.receipt_owner(&draft.id)? {
84            self.agents.deliver_outcome(draft, receipt)
85        } else {
86            self.sites.deliver_outcome(draft, receipt)
87        }
88    }
89}