auths_infra_git/
event_log.rs1use auths_core::ports::storage::{EventLogReader, EventLogWriter, StorageError};
2use auths_verifier::keri::Prefix;
3
4use crate::error::map_git2_error;
5use crate::helpers;
6use crate::repo::GitRepo;
7
8const EVENT_FILE: &str = "event.json";
9
10pub struct GitEventLog<'r> {
26 repo: &'r GitRepo,
27}
28
29impl<'r> GitEventLog<'r> {
30 pub fn new(repo: &'r GitRepo) -> Self {
31 Self { repo }
32 }
33
34 fn kel_ref(prefix: &str) -> String {
35 format!("refs/keri/{}/kel", prefix)
36 }
37}
38
39impl EventLogReader for GitEventLog<'_> {
40 fn read_event_log(&self, prefix: &Prefix) -> Result<Vec<u8>, StorageError> {
41 let refname = Self::kel_ref(prefix.as_str());
42 self.repo.with_repo(|repo| {
43 let events = walk_commits(repo, &refname)?;
44 let joined: Vec<u8> = events.into_iter().flatten().collect();
45 Ok(joined)
46 })
47 }
48
49 fn read_event_at(&self, prefix: &Prefix, seq: u64) -> Result<Vec<u8>, StorageError> {
50 let refname = Self::kel_ref(prefix.as_str());
51 self.repo.with_repo(|repo| {
52 let events = walk_commits(repo, &refname)?;
53 events
54 .into_iter()
55 .nth(seq as usize)
56 .ok_or_else(|| StorageError::not_found(format!("{}/seq/{}", prefix.as_str(), seq)))
57 })
58 }
59}
60
61impl EventLogWriter for GitEventLog<'_> {
62 fn append_event(&self, prefix: &Prefix, event: &[u8]) -> Result<(), StorageError> {
63 let refname = Self::kel_ref(prefix.as_str());
64 self.repo.with_repo(|repo| {
65 helpers::create_ref_commit(
66 repo,
67 &refname,
68 event,
69 EVENT_FILE,
70 &format!("append event to {}", prefix.as_str()),
71 )
72 .map_err(map_git2_error)?;
73 Ok(())
74 })
75 }
76}
77
78fn walk_commits(repo: &git2::Repository, refname: &str) -> Result<Vec<Vec<u8>>, StorageError> {
79 let reference = match repo.find_reference(refname) {
80 Ok(r) => r,
81 Err(e) if e.code() == git2::ErrorCode::NotFound => return Ok(Vec::new()),
82 Err(e) => return Err(map_git2_error(e)),
83 };
84
85 let mut commit = reference.peel_to_commit().map_err(map_git2_error)?;
86 let mut events = Vec::new();
87
88 loop {
89 let tree_oid = commit.tree_id();
90 match helpers::extract_blob_payload(repo, tree_oid, EVENT_FILE) {
91 Ok(data) => events.push(data),
92 Err(e) => {
93 log::warn!("skipping commit {}: {}", commit.id(), e);
94 }
95 }
96
97 if commit.parent_count() > 0 {
98 commit = commit.parent(0).map_err(map_git2_error)?;
99 } else {
100 break;
101 }
102 }
103
104 events.reverse();
105 Ok(events)
106}