Skip to main content

made_core/value_objects/ceremony/
guard_condition.rs

1use serde::{Deserialize, Serialize};
2
3use super::{
4    JoinStepCount, OutputFieldGuardCondition, StepId, StepRepeatExhaustedGuardCondition, StepStatus,
5};
6
7mod counted_join_condition;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(tag = "kind", rename_all = "snake_case")]
11pub enum GuardCondition {
12    Always,
13    AllStepsCompleted,
14    AnyStepCompleted,
15    StepsCompleted(#[serde(with = "counted_join_condition")] JoinStepCount),
16    StepStatus { step_id: StepId, status: StepStatus },
17    OutputField(OutputFieldGuardCondition),
18    StepRepeatExhausted(StepRepeatExhaustedGuardCondition),
19    HumanApproval,
20}
21
22impl GuardCondition {
23    #[must_use]
24    pub fn referenced_step_id(&self) -> Option<&StepId> {
25        match self {
26            Self::StepStatus { step_id, .. } => Some(step_id),
27            Self::OutputField(condition) => Some(condition.step_id()),
28            Self::StepRepeatExhausted(condition) => Some(condition.step_id()),
29            Self::Always
30            | Self::AllStepsCompleted
31            | Self::AnyStepCompleted
32            | Self::StepsCompleted(_)
33            | Self::HumanApproval => None,
34        }
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    #[test]
43    fn counted_join_serializes_as_tagged_object() {
44        let condition = GuardCondition::StepsCompleted(JoinStepCount::new(2).unwrap());
45        let encoded = serde_json::to_string(&condition).unwrap();
46        assert_eq!(encoded, r#"{"kind":"steps_completed","count":2}"#);
47        assert_eq!(
48            serde_json::from_str::<GuardCondition>(&encoded).unwrap(),
49            condition
50        );
51    }
52
53    #[test]
54    fn counted_join_deserialization_preserves_positive_count_invariant() {
55        for encoded in [
56            r#"{"kind":"steps_completed","count":0}"#,
57            r#"{"kind":"steps_completed","count":-1}"#,
58            r#"{"kind":"steps_completed"}"#,
59            r#"{"kind":"steps_completed","count":4294967296}"#,
60        ] {
61            assert!(
62                serde_json::from_str::<GuardCondition>(encoded).is_err(),
63                "{encoded}"
64            );
65        }
66    }
67
68    #[test]
69    fn other_guard_variants_keep_their_exact_canonical_bytes() {
70        use crate::value_objects::StepOutputField;
71        let step = StepId::new("inspect_api").unwrap();
72        let cases = [
73            (GuardCondition::Always, r#"{"kind":"always"}"#),
74            (
75                GuardCondition::AllStepsCompleted,
76                r#"{"kind":"all_steps_completed"}"#,
77            ),
78            (
79                GuardCondition::AnyStepCompleted,
80                r#"{"kind":"any_step_completed"}"#,
81            ),
82            (
83                GuardCondition::HumanApproval,
84                r#"{"kind":"human_approval"}"#,
85            ),
86            (
87                GuardCondition::StepStatus {
88                    step_id: step.clone(),
89                    status: StepStatus::Completed,
90                },
91                r#"{"kind":"step_status","step_id":"inspect_api","status":"COMPLETED"}"#,
92            ),
93            (
94                GuardCondition::OutputField(OutputFieldGuardCondition::new(
95                    step.clone(),
96                    StepOutputField::new("approved").unwrap(),
97                    serde_json::json!(true),
98                )),
99                r#"{"kind":"output_field","step_id":"inspect_api","output_field":"approved","expected":true}"#,
100            ),
101            (
102                GuardCondition::StepRepeatExhausted(StepRepeatExhaustedGuardCondition::new(step)),
103                r#"{"kind":"step_repeat_exhausted","step_id":"inspect_api"}"#,
104            ),
105        ];
106        for (condition, encoded) in cases {
107            assert_eq!(serde_json::to_string(&condition).unwrap(), encoded);
108            assert_eq!(
109                serde_json::from_str::<GuardCondition>(encoded).unwrap(),
110                condition
111            );
112        }
113    }
114}