ruvector_graph_node/
transactions.rs

1//! Transaction support for graph database operations
2
3use std::collections::HashMap;
4use uuid::Uuid;
5
6/// Transaction state
7#[derive(Debug, Clone)]
8pub enum TransactionState {
9    Active,
10    Committed,
11    RolledBack,
12}
13
14/// Transaction metadata
15#[derive(Debug, Clone)]
16pub struct Transaction {
17    pub id: String,
18    pub state: TransactionState,
19    pub operations: Vec<String>,
20}
21
22/// Transaction manager
23pub struct TransactionManager {
24    transactions: HashMap<String, Transaction>,
25}
26
27impl TransactionManager {
28    /// Create a new transaction manager
29    pub fn new() -> Self {
30        Self {
31            transactions: HashMap::new(),
32        }
33    }
34
35    /// Begin a new transaction
36    pub fn begin(&mut self) -> String {
37        let tx_id = Uuid::new_v4().to_string();
38        let tx = Transaction {
39            id: tx_id.clone(),
40            state: TransactionState::Active,
41            operations: Vec::new(),
42        };
43        self.transactions.insert(tx_id.clone(), tx);
44        tx_id
45    }
46
47    /// Commit a transaction
48    pub fn commit(&mut self, tx_id: &str) -> Result<(), String> {
49        let tx = self
50            .transactions
51            .get_mut(tx_id)
52            .ok_or_else(|| format!("Transaction not found: {}", tx_id))?;
53
54        match tx.state {
55            TransactionState::Active => {
56                tx.state = TransactionState::Committed;
57                Ok(())
58            }
59            TransactionState::Committed => Err("Transaction already committed".to_string()),
60            TransactionState::RolledBack => Err("Transaction already rolled back".to_string()),
61        }
62    }
63
64    /// Rollback a transaction
65    pub fn rollback(&mut self, tx_id: &str) -> Result<(), String> {
66        let tx = self
67            .transactions
68            .get_mut(tx_id)
69            .ok_or_else(|| format!("Transaction not found: {}", tx_id))?;
70
71        match tx.state {
72            TransactionState::Active => {
73                tx.state = TransactionState::RolledBack;
74                Ok(())
75            }
76            TransactionState::Committed => Err("Cannot rollback committed transaction".to_string()),
77            TransactionState::RolledBack => Err("Transaction already rolled back".to_string()),
78        }
79    }
80
81    /// Add an operation to a transaction
82    pub fn add_operation(&mut self, tx_id: &str, operation: String) -> Result<(), String> {
83        let tx = self
84            .transactions
85            .get_mut(tx_id)
86            .ok_or_else(|| format!("Transaction not found: {}", tx_id))?;
87
88        match tx.state {
89            TransactionState::Active => {
90                tx.operations.push(operation);
91                Ok(())
92            }
93            _ => Err("Transaction is not active".to_string()),
94        }
95    }
96
97    /// Get transaction state
98    pub fn get_state(&self, tx_id: &str) -> Option<TransactionState> {
99        self.transactions.get(tx_id).map(|tx| tx.state.clone())
100    }
101
102    /// Clean up old transactions
103    pub fn cleanup(&mut self) {
104        self.transactions
105            .retain(|_, tx| matches!(tx.state, TransactionState::Active));
106    }
107}
108
109impl Default for TransactionManager {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_transaction_lifecycle() {
121        let mut tm = TransactionManager::new();
122
123        // Begin transaction
124        let tx_id = tm.begin();
125        assert!(matches!(
126            tm.get_state(&tx_id),
127            Some(TransactionState::Active)
128        ));
129
130        // Add operation
131        tm.add_operation(&tx_id, "CREATE NODE".to_string()).unwrap();
132
133        // Commit
134        tm.commit(&tx_id).unwrap();
135        assert!(matches!(
136            tm.get_state(&tx_id),
137            Some(TransactionState::Committed)
138        ));
139
140        // Cannot commit again
141        assert!(tm.commit(&tx_id).is_err());
142    }
143
144    #[test]
145    fn test_transaction_rollback() {
146        let mut tm = TransactionManager::new();
147
148        let tx_id = tm.begin();
149        tm.add_operation(&tx_id, "CREATE NODE".to_string()).unwrap();
150
151        // Rollback
152        tm.rollback(&tx_id).unwrap();
153        assert!(matches!(
154            tm.get_state(&tx_id),
155            Some(TransactionState::RolledBack)
156        ));
157
158        // Cannot rollback again
159        assert!(tm.rollback(&tx_id).is_err());
160    }
161}