Skip to main content

made_core/value_objects/ceremony/
delivery_recipient.rs

1use serde::{Deserialize, Serialize};
2
3use crate::value_objects::{CeremonyAgentExecutionId, HostAgentIncarnation, HostDeliveryTarget};
4
5use super::RoleId;
6
7/// The one live agent an intervention was put in front of.
8///
9/// Three parts because all three are needed to say "this one and not
10/// that one": the execution names the piece of work, the incarnation
11/// names the process generation doing it, and the role names the seat
12/// it holds. Drop the incarnation and a replaced agent answers for its
13/// predecessor; drop the role and the answer cannot be checked against
14/// the item's target.
15#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
16pub struct DeliveryRecipient {
17    agent_execution_id: CeremonyAgentExecutionId,
18    incarnation: HostAgentIncarnation,
19    role_id: RoleId,
20}
21
22impl DeliveryRecipient {
23    #[must_use]
24    pub const fn new(
25        agent_execution_id: CeremonyAgentExecutionId,
26        incarnation: HostAgentIncarnation,
27        role_id: RoleId,
28    ) -> Self {
29        Self {
30            agent_execution_id,
31            incarnation,
32            role_id,
33        }
34    }
35
36    #[must_use]
37    pub const fn agent_execution_id(&self) -> &CeremonyAgentExecutionId {
38        &self.agent_execution_id
39    }
40
41    #[must_use]
42    pub const fn incarnation(&self) -> &HostAgentIncarnation {
43        &self.incarnation
44    }
45
46    #[must_use]
47    pub const fn role_id(&self) -> &RoleId {
48        &self.role_id
49    }
50
51    /// The ledger destination that names exactly this recipient.
52    #[must_use]
53    pub fn exact_target(&self) -> HostDeliveryTarget {
54        HostDeliveryTarget::agent_execution(
55            self.agent_execution_id.clone(),
56            self.incarnation.clone(),
57        )
58    }
59
60    /// The ledger destination for the seat, whoever is sitting in it.
61    #[must_use]
62    pub fn role_target(&self) -> HostDeliveryTarget {
63        HostDeliveryTarget::role(self.role_id.clone())
64    }
65
66    /// Whether this is the same execution *and* the same generation.
67    ///
68    /// The comparison an exact target is checked with: a new process
69    /// picking up the same execution is a different recipient, and
70    /// letting it acknowledge would credit one agent with another's
71    /// answer.
72    #[must_use]
73    pub fn is_same_incarnation_as(&self, other: &Self) -> bool {
74        self.agent_execution_id == other.agent_execution_id && self.incarnation == other.incarnation
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    fn recipient(execution: &str, incarnation: &str) -> DeliveryRecipient {
83        DeliveryRecipient::new(
84            CeremonyAgentExecutionId::new(execution).unwrap(),
85            HostAgentIncarnation::new(incarnation).unwrap(),
86            RoleId::new("ENGINEER").unwrap(),
87        )
88    }
89
90    #[test]
91    fn a_replacement_process_is_not_the_same_recipient() {
92        let first = recipient("exec-1", "inc-1");
93        assert!(first.is_same_incarnation_as(&recipient("exec-1", "inc-1")));
94        assert!(!first.is_same_incarnation_as(&recipient("exec-1", "inc-2")));
95        assert!(!first.is_same_incarnation_as(&recipient("exec-2", "inc-1")));
96    }
97
98    #[test]
99    fn it_names_both_the_process_and_the_seat_as_destinations() {
100        let recipient = recipient("exec-1", "inc-1");
101        assert_eq!(
102            recipient.exact_target().target_key().as_str(),
103            "agent:exec-1:inc-1"
104        );
105        assert_eq!(
106            recipient.role_target().target_key().as_str(),
107            "role:ENGINEER"
108        );
109    }
110}