Skip to main content

ledvar_diff/
status.rs

1//! The five exhaustive comparison outcomes (DIFF.md §2).
2
3use serde::{Deserialize, Serialize};
4
5/// The relationship of a node to its prior state. Exhaustive and mutually
6/// exclusive: every node is exactly one, and there is no sixth case.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8pub enum StateStatus {
9    /// No previous snapshot (cold start).
10    Baseline,
11    /// Present in current, not in previous.
12    Added,
13    /// Present in previous, not in current.
14    Removed,
15    /// Present in both, same `content_hash`.
16    Unchanged,
17    /// Present in both, different `content_hash`.
18    Modified,
19}
20
21impl StateStatus {
22    /// A real change relative to the previous state (`Added`, `Removed`, `Modified`).
23    /// `Baseline` and `Unchanged` are not drift.
24    pub fn is_drift(self) -> bool {
25        matches!(
26            self,
27            StateStatus::Added | StateStatus::Removed | StateStatus::Modified
28        )
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35
36    #[test]
37    fn drift_classification() {
38        assert!(StateStatus::Added.is_drift());
39        assert!(StateStatus::Removed.is_drift());
40        assert!(StateStatus::Modified.is_drift());
41        assert!(!StateStatus::Unchanged.is_drift());
42        assert!(!StateStatus::Baseline.is_drift());
43    }
44
45    #[test]
46    fn every_status_serializes_to_its_name() {
47        // The five names are the companion standard's vocabulary (DIFF.md §2) — pin the wire form.
48        for (status, name) in [
49            (StateStatus::Baseline, "\"Baseline\""),
50            (StateStatus::Added, "\"Added\""),
51            (StateStatus::Removed, "\"Removed\""),
52            (StateStatus::Unchanged, "\"Unchanged\""),
53            (StateStatus::Modified, "\"Modified\""),
54        ] {
55            assert_eq!(serde_json::to_string(&status).unwrap(), name);
56        }
57    }
58}