made-core 0.7.3

Domain core of MADE: entities, value objects, events, ports. No IO.
Documentation
//! [`CeremonyStepContribution`] — one agent-produced intervention in a
//! ceremony's running transcript.
//!
//! A contribution records that a given role, executing a given step,
//! produced a [`StepOutput`]. It is the unit a context store accumulates
//! so that later steps can deliberate with the earlier interventions in
//! view — turning a sequence of isolated steps into one conversation.

use serde::{Deserialize, Serialize};

use super::{RoleId, StateIteration, StateVisit, StepId, StepOutput};

/// One intervention produced during a ceremony step.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CeremonyStepContribution {
    step_id: StepId,
    #[serde(default, skip_serializing_if = "StateIteration::is_first")]
    state_iteration: StateIteration,
    #[serde(default, skip_serializing_if = "StateVisit::is_first")]
    state_visit: StateVisit,
    role_id: RoleId,
    output: StepOutput,
}

impl CeremonyStepContribution {
    /// Record the `output` produced by `role_id` while executing `step_id`.
    #[must_use]
    pub fn new(step_id: StepId, role_id: RoleId, output: StepOutput) -> Self {
        Self::at_state_iteration(step_id, StateIteration::FIRST, role_id, output)
    }

    #[must_use]
    pub fn at_state_iteration(
        step_id: StepId,
        state_iteration: StateIteration,
        role_id: RoleId,
        output: StepOutput,
    ) -> Self {
        Self {
            step_id,
            state_iteration,
            state_visit: StateVisit::FIRST,
            role_id,
            output,
        }
    }

    /// The step that produced this contribution.
    #[must_use]
    pub fn step_id(&self) -> &StepId {
        &self.step_id
    }

    #[must_use]
    pub fn state_visit(&self) -> StateVisit {
        self.state_visit
    }

    #[must_use]
    pub fn with_state_visit(mut self, state_visit: StateVisit) -> Self {
        self.state_visit = state_visit;
        self
    }

    #[must_use]
    pub fn state_iteration(&self) -> StateIteration {
        self.state_iteration
    }

    /// The role that produced this contribution.
    #[must_use]
    pub fn role_id(&self) -> &RoleId {
        &self.role_id
    }

    /// The output the step produced.
    #[must_use]
    pub fn output(&self) -> &StepOutput {
        &self.output
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;

    use serde_json::json;

    use super::*;
    use crate::value_objects::Attributes;

    fn contribution() -> CeremonyStepContribution {
        let output = StepOutput::new(
            Attributes::new(BTreeMap::from([(
                "winner_content".to_owned(),
                json!("We should ship the smaller scope first."),
            )]))
            .unwrap(),
        );
        CeremonyStepContribution::new(
            StepId::new("open_room").unwrap(),
            RoleId::new("FACILITATOR").unwrap(),
            output,
        )
    }

    #[test]
    fn exposes_its_components() {
        let contribution = contribution();

        assert_eq!(contribution.step_id().as_str(), "open_room");
        assert_eq!(contribution.role_id().as_str(), "FACILITATOR");
        assert_eq!(
            contribution
                .output()
                .attributes()
                .get("winner_content")
                .and_then(serde_json::Value::as_str),
            Some("We should ship the smaller scope first.")
        );
    }

    #[test]
    fn serde_roundtrips() {
        let contribution = contribution();
        let json = serde_json::to_string(&contribution).unwrap();
        let restored: CeremonyStepContribution = serde_json::from_str(&json).unwrap();

        assert_eq!(restored, contribution);
    }
}