meerkat_runtime/
input_ledger.rs1use indexmap::IndexMap;
6use meerkat_core::lifecycle::InputId;
7
8use crate::input_state::InputState;
9
10#[derive(Debug, Default, Clone)]
12pub struct InputLedger {
13 states: IndexMap<InputId, InputState>,
15}
16
17impl InputLedger {
18 pub fn new() -> Self {
20 Self::default()
21 }
22
23 pub fn accept(&mut self, state: InputState) {
25 self.states.insert(state.input_id.clone(), state);
26 }
27
28 pub fn recover(&mut self, state: InputState) -> bool {
34 self.states.insert(state.input_id.clone(), state);
35 true
36 }
37
38 pub fn get(&self, input_id: &InputId) -> Option<&InputState> {
40 self.states.get(input_id)
41 }
42
43 pub fn remove(&mut self, input_id: &InputId) -> Option<InputState> {
45 self.states.shift_remove(input_id)
46 }
47
48 pub fn get_mut(&mut self, input_id: &InputId) -> Option<&mut InputState> {
50 self.states.get_mut(input_id)
51 }
52
53 pub fn iter(&self) -> impl Iterator<Item = (&InputId, &InputState)> {
57 self.states.iter()
58 }
59
60 pub fn len(&self) -> usize {
62 self.states.len()
63 }
64
65 pub fn is_empty(&self) -> bool {
67 self.states.is_empty()
68 }
69}
70
71#[cfg(test)]
72#[allow(clippy::unwrap_used)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn accept_and_retrieve() {
78 let mut ledger = InputLedger::new();
79 let id = InputId::new();
80 let state = InputState::new_accepted(id.clone());
81 ledger.accept(state);
82
83 assert_eq!(ledger.len(), 1);
84 assert!(!ledger.is_empty());
85 let retrieved = ledger.get(&id).unwrap();
86 assert_eq!(retrieved.input_id, id);
87 }
88
89 #[test]
90 fn accept_preserves_idempotency_key_as_metadata_only() {
91 let mut ledger = InputLedger::new();
92
93 let input_id = InputId::new();
94 let mut state = InputState::new_accepted(input_id.clone());
95 state.idempotency_key = Some(crate::identifiers::IdempotencyKey::new("req-123"));
96 ledger.accept(state);
97
98 assert_eq!(ledger.len(), 1);
99 assert_eq!(
100 ledger
101 .get(&input_id)
102 .and_then(|state| state.idempotency_key.as_ref()),
103 Some(&crate::identifiers::IdempotencyKey::new("req-123"))
104 );
105 }
106
107 #[test]
108 fn recover_does_not_interpret_durability() {
109 let mut ledger = InputLedger::new();
110
111 let mut state = InputState::new_accepted(InputId::new());
112 state.durability = Some(crate::input::InputDurability::Ephemeral);
113 assert!(
114 ledger.recover(state),
115 "the ledger inserts rows after machine authority has made the retention decision"
116 );
117 assert_eq!(ledger.len(), 1);
118 }
119}