use serde::{Deserialize, Serialize};
use crate::{contracts::TxCtx, debug};
use super::{
read_field, storage_has_key, storage_keys::BASE_KEY_ACTION_LOG, storage_remove, write_field,
};
pub const SUB_KEY_LOG_LEN: u64 = u64::MAX;
pub struct ActionLog {
len_commited: u64,
buffer: Option<ActionRecord>,
}
#[derive(Serialize, Deserialize)]
pub struct ActionRecord {
pub tx_ctx: TxCtx,
#[serde(with = "serde_bytes")]
pub value: Vec<u8>,
pub commited: u64,
}
impl ActionLog {
pub fn open() -> Self {
let len: u64 = if let Some(len) = read_field(BASE_KEY_ACTION_LOG, SUB_KEY_LOG_LEN) {
debug!("Open existing action log. len={len}");
len
} else {
debug!("Create new action log. len=0");
write_field(BASE_KEY_ACTION_LOG, SUB_KEY_LOG_LEN, &0u64);
0
};
Self {
len_commited: len,
buffer: None,
}
}
pub fn exists() -> bool {
storage_has_key(BASE_KEY_ACTION_LOG, SUB_KEY_LOG_LEN)
}
pub fn push(&mut self, value_bytes: Vec<u8>, tx_ctx: TxCtx) {
debug_assert!(self.len_commited < SUB_KEY_LOG_LEN);
assert!(self.buffer.is_none(), "can only add one event at a time");
self.buffer = Some(ActionRecord {
tx_ctx,
value: value_bytes,
commited: 0,
});
}
pub fn commit(self) {
assert!(
self.buffer.is_some(),
"commit must only be called, if there was an event"
);
let full_len = self.len_commited + 1;
let sub_key = self.len_commited;
let mut value = self.buffer.unwrap();
write_field(BASE_KEY_ACTION_LOG, sub_key, &value);
write_field(BASE_KEY_ACTION_LOG, SUB_KEY_LOG_LEN, &full_len);
debug!("Commited action to log. len={full_len}");
}
pub fn clear(&self) {
let mut action_sub_key = 0;
while storage_has_key(BASE_KEY_ACTION_LOG, action_sub_key) {
storage_remove(BASE_KEY_ACTION_LOG, action_sub_key);
action_sub_key += 1;
}
}
pub fn get(&self, idx: usize) -> Option<ActionRecord> {
let idx = idx as u64;
debug_assert!(idx < SUB_KEY_LOG_LEN);
if idx < self.len_commited {
read_field(BASE_KEY_ACTION_LOG, idx)
} else {
None
}
}
pub fn iter(&self) -> Iter<'_> {
Iter { log: self, idx: 0 }
}
}
pub struct Iter<'a> {
log: &'a ActionLog,
idx: usize,
}
impl<'a> Iterator for Iter<'a> {
type Item = ActionRecord;
fn next(&mut self) -> Option<Self::Item> {
let idx = self.idx;
self.idx += 1;
self.log.get(idx)
}
}