Skip to main content

oxihuman_core/
event_sourcing.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5pub struct EventStore {
6    pub events: Vec<(String, u64, String)>,
7}
8
9pub fn new_event_store() -> EventStore {
10    EventStore { events: Vec::new() }
11}
12
13pub fn es_append(s: &mut EventStore, agg_id: &str, version: u64, event_json: &str) {
14    s.events
15        .push((agg_id.to_string(), version, event_json.to_string()));
16}
17
18pub fn es_events_for<'a>(s: &'a EventStore, agg_id: &str) -> Vec<(u64, &'a str)> {
19    s.events
20        .iter()
21        .filter(|(id, _, _)| id == agg_id)
22        .map(|(_, v, j)| (*v, j.as_str()))
23        .collect()
24}
25
26pub fn es_latest_version(s: &EventStore, agg_id: &str) -> u64 {
27    s.events
28        .iter()
29        .filter(|(id, _, _)| id == agg_id)
30        .map(|(_, v, _)| *v)
31        .max()
32        .unwrap_or(0)
33}
34
35pub fn es_total_events(s: &EventStore) -> usize {
36    s.events.len()
37}
38
39pub fn es_replay(s: &EventStore, agg_id: &str) -> Vec<String> {
40    let mut evs: Vec<(u64, String)> = s
41        .events
42        .iter()
43        .filter(|(id, _, _)| id == agg_id)
44        .map(|(_, v, j)| (*v, j.clone()))
45        .collect();
46    evs.sort_by_key(|(v, _)| *v);
47    evs.into_iter().map(|(_, j)| j).collect()
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn test_append_and_total() {
56        /* append events and count total */
57        let mut s = new_event_store();
58        es_append(&mut s, "agg1", 1, "{}");
59        es_append(&mut s, "agg1", 2, "{}");
60        assert_eq!(es_total_events(&s), 2);
61    }
62
63    #[test]
64    fn test_events_for() {
65        /* filter events by aggregate */
66        let mut s = new_event_store();
67        es_append(&mut s, "a", 1, "ev1");
68        es_append(&mut s, "b", 1, "ev2");
69        let evs = es_events_for(&s, "a");
70        assert_eq!(evs.len(), 1);
71        assert_eq!(evs[0].1, "ev1");
72    }
73
74    #[test]
75    fn test_latest_version() {
76        /* latest version is max version for aggregate */
77        let mut s = new_event_store();
78        es_append(&mut s, "agg", 1, "");
79        es_append(&mut s, "agg", 3, "");
80        es_append(&mut s, "agg", 2, "");
81        assert_eq!(es_latest_version(&s, "agg"), 3);
82    }
83
84    #[test]
85    fn test_latest_version_missing() {
86        /* missing aggregate returns 0 */
87        let s = new_event_store();
88        assert_eq!(es_latest_version(&s, "missing"), 0);
89    }
90
91    #[test]
92    fn test_replay_sorted() {
93        /* replay returns events in version order */
94        let mut s = new_event_store();
95        es_append(&mut s, "agg", 2, "second");
96        es_append(&mut s, "agg", 1, "first");
97        let replayed = es_replay(&s, "agg");
98        assert_eq!(replayed[0], "first");
99        assert_eq!(replayed[1], "second");
100    }
101
102    #[test]
103    fn test_empty_store() {
104        /* empty store returns zero events */
105        let s = new_event_store();
106        assert_eq!(es_total_events(&s), 0);
107    }
108
109    #[test]
110    fn test_multiple_aggregates() {
111        /* multiple aggregates stored independently */
112        let mut s = new_event_store();
113        es_append(&mut s, "a", 1, "a1");
114        es_append(&mut s, "b", 1, "b1");
115        es_append(&mut s, "a", 2, "a2");
116        assert_eq!(es_events_for(&s, "a").len(), 2);
117        assert_eq!(es_events_for(&s, "b").len(), 1);
118    }
119}