Skip to main content

weavatrix_memory/projection/memory/
apply.rs

1use super::{MemoryProjection, state::NodeRevision};
2use crate::{
3    domain::MemoryEvent,
4    error::{MemoryError, Result},
5    event::StoredEvent,
6    projection::Projection,
7};
8
9impl Projection<MemoryEvent> for MemoryProjection {
10    fn prepare_replay(&mut self, events: &[StoredEvent<MemoryEvent>]) {
11        let mut nodes = 0;
12        let mut facts = 0;
13        for event in events {
14            match event.payload {
15                MemoryEvent::NodeUpserted { .. } => nodes += 1,
16                MemoryEvent::FactRecorded { .. } => facts += 1,
17                MemoryEvent::FactRetracted { .. } => {}
18            }
19        }
20        self.nodes.reserve(nodes);
21        self.facts.reserve(facts);
22        self.node_lookup.reserve(nodes);
23        self.fact_lookup.reserve(facts);
24        self.incident_offsets.reserve(nodes);
25    }
26
27    fn apply(&mut self, event: &StoredEvent<MemoryEvent>) -> Result<()> {
28        if event.metadata.event_type != event.payload.event_type() {
29            return Err(MemoryError::InvalidValue {
30                field: "event_type",
31                reason: "must match the memory event payload",
32            });
33        }
34        match &event.payload {
35            MemoryEvent::NodeUpserted { node } => self.insert_node(NodeRevision {
36                node: node.clone(),
37                recorded_at: event.metadata.recorded_at,
38                position: event.metadata.global_position,
39            })?,
40            MemoryEvent::FactRecorded { fact } => {
41                if fact.recorded_at != event.metadata.recorded_at
42                    || fact.agent_id != event.metadata.agent_id
43                    || fact.session_id != event.metadata.session_id
44                {
45                    return Err(MemoryError::InvalidValue {
46                        field: "fact.envelope",
47                        reason: "fact provenance must match its event envelope",
48                    });
49                }
50                self.insert_fact(fact.clone())?;
51            }
52            MemoryEvent::FactRetracted {
53                fact_id,
54                valid_until,
55                evidence,
56            } => {
57                self.apply_retraction(event.metadata.recorded_at, fact_id, *valid_until, evidence)?;
58            }
59        }
60        self.last_global_position = Some(event.metadata.global_position);
61        Ok(())
62    }
63
64    fn apply_owned(&mut self, event: StoredEvent<MemoryEvent>) -> Result<()> {
65        let StoredEvent { metadata, payload } = event;
66        if metadata.event_type != payload.event_type() {
67            return Err(MemoryError::InvalidValue {
68                field: "event_type",
69                reason: "must match the memory event payload",
70            });
71        }
72        match payload {
73            MemoryEvent::NodeUpserted { node } => self.insert_node(NodeRevision {
74                node,
75                recorded_at: metadata.recorded_at,
76                position: metadata.global_position,
77            })?,
78            MemoryEvent::FactRecorded { fact } => {
79                if fact.recorded_at != metadata.recorded_at
80                    || fact.agent_id != metadata.agent_id
81                    || fact.session_id != metadata.session_id
82                {
83                    return Err(MemoryError::InvalidValue {
84                        field: "fact.envelope",
85                        reason: "fact provenance must match its event envelope",
86                    });
87                }
88                self.insert_fact(fact)?;
89            }
90            MemoryEvent::FactRetracted {
91                fact_id,
92                valid_until,
93                evidence,
94            } => {
95                self.apply_retraction(metadata.recorded_at, &fact_id, valid_until, &evidence)?;
96            }
97        }
98        self.last_global_position = Some(metadata.global_position);
99        Ok(())
100    }
101}