salamander/agent/kv.rs
1//! `KvProjection`: a `BTreeMap<String, Vec<u8>>` folded over `Put`/`Delete`
2//! events. The engine-correctness workhorse; used by the crash harness.
3//!
4//! Kept in the `agent` module for now (Phase 1.5 spec, WP-1 step 4): its
5//! `Put`/`Delete` live in `EventBody`, so it folds `EventBody` like the
6//! session view does. The query layer's `IndexedView` (WP-3) supersedes it
7//! as the general-purpose, payload-agnostic store.
8
9use std::collections::BTreeMap;
10
11use super::EventBody;
12use crate::event::Event;
13use crate::projection::Projection;
14
15/// A last-write-wins key/value store folded from [`EventBody::Put`] and
16/// [`EventBody::Delete`] events. The simplest built-in projection; the
17/// query layer's [`crate::IndexedView`] is the general-purpose store.
18#[derive(Debug, Default)]
19pub struct KvProjection {
20 state: BTreeMap<String, Vec<u8>>,
21 cursor: u64,
22}
23
24impl Projection for KvProjection {
25 type Body = EventBody;
26 type State = BTreeMap<String, Vec<u8>>;
27
28 fn apply(&mut self, event: &Event<EventBody>) {
29 match &event.body {
30 EventBody::Put { key, value } => {
31 self.state.insert(key.clone(), value.clone());
32 }
33 EventBody::Delete { key } => {
34 self.state.remove(key);
35 }
36 _ => {}
37 }
38 self.cursor = event.offset + 1;
39 }
40
41 fn cursor(&self) -> u64 {
42 self.cursor
43 }
44
45 fn state(&self) -> &Self::State {
46 &self.state
47 }
48}