use crate::metadata::tables::TableDataOwned;
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone)]
pub enum Operation {
Insert(u32, TableDataOwned),
Update(u32, TableDataOwned),
Delete(u32),
}
impl Operation {
pub fn get_rid(&self) -> u32 {
match self {
Operation::Insert(rid, _) | Operation::Update(rid, _) | Operation::Delete(rid) => *rid,
}
}
pub fn get_row_data(&self) -> Option<&TableDataOwned> {
match self {
Operation::Insert(_, data) | Operation::Update(_, data) => Some(data),
Operation::Delete(_) => None,
}
}
pub fn operation_type(&self) -> &'static str {
match self {
Operation::Insert(_, _) => "Insert",
Operation::Update(_, _) => "Update",
Operation::Delete(_) => "Delete",
}
}
}
#[derive(Debug, Clone)]
pub struct TableOperation {
pub timestamp: u64,
pub operation: Operation,
}
impl TableOperation {
pub fn new(operation: Operation) -> Self {
Self {
timestamp: Self::current_timestamp_micros(),
operation,
}
}
pub fn new_with_timestamp(operation: Operation, timestamp: u64) -> Self {
Self {
timestamp,
operation,
}
}
pub fn get_rid(&self) -> u32 {
self.operation.get_rid()
}
pub fn is_insert(&self) -> bool {
matches!(self.operation, Operation::Insert(_, _))
}
pub fn is_update(&self) -> bool {
matches!(self.operation, Operation::Update(_, _))
}
pub fn is_delete(&self) -> bool {
matches!(self.operation, Operation::Delete(_))
}
#[allow(clippy::cast_possible_truncation)] fn current_timestamp_micros() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_micros() as u64
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test::factories::table::cilassembly::create_test_row;
#[test]
fn test_operation_get_rid_for_all_variants() {
let row_data = create_test_row();
let insert_op = Operation::Insert(42, row_data.clone());
assert_eq!(insert_op.get_rid(), 42);
let update_op = Operation::Update(100, row_data);
assert_eq!(update_op.get_rid(), 100);
let delete_op = Operation::Delete(10);
assert_eq!(delete_op.get_rid(), 10);
}
#[test]
fn test_operation_get_row_data_for_all_variants() {
let row_data = create_test_row();
let insert_op = Operation::Insert(1, row_data.clone());
assert!(insert_op.get_row_data().is_some());
let update_op = Operation::Update(1, row_data);
assert!(update_op.get_row_data().is_some());
let delete_op = Operation::Delete(1);
assert!(delete_op.get_row_data().is_none());
}
#[test]
fn test_operation_type_for_all_variants() {
let row_data = create_test_row();
let insert_op = Operation::Insert(1, row_data.clone());
assert_eq!(insert_op.operation_type(), "Insert");
let update_op = Operation::Update(1, row_data);
assert_eq!(update_op.operation_type(), "Update");
let delete_op = Operation::Delete(1);
assert_eq!(delete_op.operation_type(), "Delete");
}
#[test]
fn test_operation_edge_case_rid_zero() {
let delete_op = Operation::Delete(0);
assert_eq!(delete_op.get_rid(), 0);
}
#[test]
fn test_operation_edge_case_max_rid() {
let delete_op = Operation::Delete(u32::MAX);
assert_eq!(delete_op.get_rid(), u32::MAX);
}
#[test]
fn test_table_operation_new_captures_timestamp() {
let op = TableOperation::new(Operation::Delete(1));
assert!(op.timestamp > 0);
}
#[test]
fn test_table_operation_new_with_timestamp() {
let op = TableOperation::new_with_timestamp(Operation::Delete(1), 12345);
assert_eq!(op.timestamp, 12345);
}
#[test]
fn test_table_operation_timestamp_ordering() {
let op1 = TableOperation::new(Operation::Delete(1));
std::thread::sleep(std::time::Duration::from_micros(10));
let op2 = TableOperation::new(Operation::Delete(2));
assert!(op2.timestamp > op1.timestamp);
}
#[test]
fn test_table_operation_get_rid() {
let row_data = create_test_row();
let op = TableOperation::new(Operation::Insert(999, row_data));
assert_eq!(op.get_rid(), 999);
}
#[test]
fn test_table_operation_is_insert() {
let row_data = create_test_row();
let insert_op = TableOperation::new(Operation::Insert(1, row_data.clone()));
assert!(insert_op.is_insert());
assert!(!insert_op.is_update());
assert!(!insert_op.is_delete());
let update_op = TableOperation::new(Operation::Update(1, row_data));
assert!(!update_op.is_insert());
let delete_op = TableOperation::new(Operation::Delete(1));
assert!(!delete_op.is_insert());
}
#[test]
fn test_table_operation_is_update() {
let row_data = create_test_row();
let update_op = TableOperation::new(Operation::Update(1, row_data.clone()));
assert!(update_op.is_update());
assert!(!update_op.is_insert());
assert!(!update_op.is_delete());
let insert_op = TableOperation::new(Operation::Insert(1, row_data));
assert!(!insert_op.is_update());
let delete_op = TableOperation::new(Operation::Delete(1));
assert!(!delete_op.is_update());
}
#[test]
fn test_table_operation_is_delete() {
let row_data = create_test_row();
let delete_op = TableOperation::new(Operation::Delete(1));
assert!(delete_op.is_delete());
assert!(!delete_op.is_insert());
assert!(!delete_op.is_update());
let insert_op = TableOperation::new(Operation::Insert(1, row_data.clone()));
assert!(!insert_op.is_delete());
let update_op = TableOperation::new(Operation::Update(1, row_data));
assert!(!update_op.is_delete());
}
#[test]
fn test_table_operation_clone() {
let row_data = create_test_row();
let op = TableOperation::new_with_timestamp(Operation::Insert(42, row_data), 5000);
let cloned = op.clone();
assert_eq!(cloned.timestamp, 5000);
assert_eq!(cloned.get_rid(), 42);
assert!(cloned.is_insert());
}
}