Skip to main content

made_core/value_objects/ceremony/
ceremony_transcript.rs

1//! [`CeremonyTranscript`] — the ordered record of every intervention a
2//! ceremony has produced so far.
3//!
4//! The transcript is what makes a ceremony a conversation rather than a
5//! sequence of monologues: the context store accumulates it and the
6//! engine hands it to each step so later interventions can build on the
7//! earlier ones.
8
9use serde::{Deserialize, Serialize};
10
11use super::CeremonyStepContribution;
12
13/// The ordered list of contributions accumulated during a ceremony.
14#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(transparent)]
16pub struct CeremonyTranscript(Vec<CeremonyStepContribution>);
17
18impl CeremonyTranscript {
19    /// Build a transcript from an ordered list of contributions.
20    #[must_use]
21    pub fn new(contributions: Vec<CeremonyStepContribution>) -> Self {
22        Self(contributions)
23    }
24
25    /// An empty transcript — a ceremony that has produced nothing yet.
26    #[must_use]
27    pub fn empty() -> Self {
28        Self::default()
29    }
30
31    /// Return this transcript extended with `contribution` appended last.
32    #[must_use]
33    pub fn appended(mut self, contribution: CeremonyStepContribution) -> Self {
34        self.0.push(contribution);
35        self
36    }
37
38    /// The contributions in the order they were produced.
39    #[must_use]
40    pub fn contributions(&self) -> &[CeremonyStepContribution] {
41        &self.0
42    }
43
44    /// Whether the transcript holds no contributions.
45    #[must_use]
46    pub fn is_empty(&self) -> bool {
47        self.0.is_empty()
48    }
49
50    /// The number of contributions recorded.
51    #[must_use]
52    pub fn len(&self) -> usize {
53        self.0.len()
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use crate::value_objects::{RoleId, StepId, StepOutput};
61
62    fn contribution(step: &str, role: &str) -> CeremonyStepContribution {
63        CeremonyStepContribution::new(
64            StepId::new(step).unwrap(),
65            RoleId::new(role).unwrap(),
66            StepOutput::empty(),
67        )
68    }
69
70    #[test]
71    fn empty_transcript_has_no_contributions() {
72        let transcript = CeremonyTranscript::empty();
73
74        assert!(transcript.is_empty());
75        assert_eq!(transcript.len(), 0);
76        assert!(transcript.contributions().is_empty());
77    }
78
79    #[test]
80    fn appended_preserves_order() {
81        let transcript = CeremonyTranscript::empty()
82            .appended(contribution("open_room", "FACILITATOR"))
83            .appended(contribution("customer_story", "CUSTOMER_ADVOCATE"));
84
85        assert_eq!(transcript.len(), 2);
86        assert!(!transcript.is_empty());
87        assert_eq!(
88            transcript.contributions()[0].step_id().as_str(),
89            "open_room"
90        );
91        assert_eq!(
92            transcript.contributions()[1].step_id().as_str(),
93            "customer_story"
94        );
95    }
96}