use super::types::ChunkId;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TransactionStatus {
Pending,
Committed,
Aborted,
}
#[derive(Debug, Clone)]
pub enum Operation {
AddChunk { chunk_id: ChunkId, data: Vec<u8> },
UpdateChunk { chunk_id: ChunkId, data: Vec<u8> },
RemoveChunk { chunk_id: ChunkId },
AddFile { path: String, chunks: Vec<ChunkId> },
UpdateFile { path: String, chunks: Vec<ChunkId> },
RemoveFile { path: String },
BundleRoot { chunk_ids: Vec<ChunkId> },
}
#[derive(Debug, Clone)]
pub struct Transaction {
pub id: u64,
pub engram_version: u64,
pub operations: Vec<Operation>,
pub timestamp: Instant,
pub status: TransactionStatus,
}
impl Transaction {
pub fn new(id: u64, engram_version: u64) -> Self {
Self {
id,
engram_version,
operations: Vec::new(),
timestamp: Instant::now(),
status: TransactionStatus::Pending,
}
}
pub fn add_operation(&mut self, op: Operation) {
if self.status == TransactionStatus::Pending {
self.operations.push(op);
}
}
pub fn commit(&mut self) {
if self.status == TransactionStatus::Pending {
self.status = TransactionStatus::Committed;
}
}
pub fn abort(&mut self) {
if self.status == TransactionStatus::Pending {
self.status = TransactionStatus::Aborted;
}
}
pub fn age(&self) -> std::time::Duration {
Instant::now().duration_since(self.timestamp)
}
}
pub struct TransactionManager {
next_id: AtomicU64,
}
impl TransactionManager {
pub fn new() -> Self {
Self {
next_id: AtomicU64::new(0),
}
}
pub fn begin(&self, engram_version: u64) -> Transaction {
let id = self.next_id.fetch_add(1, Ordering::AcqRel);
Transaction::new(id, engram_version)
}
}
impl Default for TransactionManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_transaction_creation() {
let tx = Transaction::new(1, 0);
assert_eq!(tx.id, 1);
assert_eq!(tx.engram_version, 0);
assert_eq!(tx.status, TransactionStatus::Pending);
assert!(tx.operations.is_empty());
}
#[test]
fn test_transaction_operations() {
let mut tx = Transaction::new(1, 0);
tx.add_operation(Operation::AddChunk {
chunk_id: 1,
data: vec![1, 2, 3],
});
assert_eq!(tx.operations.len(), 1);
}
#[test]
fn test_transaction_commit() {
let mut tx = Transaction::new(1, 0);
tx.commit();
assert_eq!(tx.status, TransactionStatus::Committed);
tx.add_operation(Operation::AddChunk {
chunk_id: 1,
data: vec![],
});
assert_eq!(tx.operations.len(), 0);
}
#[test]
fn test_transaction_manager() {
let manager = TransactionManager::new();
let tx1 = manager.begin(0);
let tx2 = manager.begin(1);
assert_eq!(tx1.id, 0);
assert_eq!(tx2.id, 1);
}
}