vapor_cli/
transactions.rs

1use anyhow::{Context, Result};
2use rusqlite::Connection;
3use std::sync::{Arc, Mutex};
4
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub enum TransactionState {
7    None,
8    Active,
9}
10
11pub struct TransactionManager {
12    state: Arc<Mutex<TransactionState>>,
13}
14
15impl TransactionManager {
16    pub fn new() -> Self {
17        Self {
18            state: Arc::new(Mutex::new(TransactionState::None)),
19        }
20    }
21
22    pub fn begin_transaction(&self, conn: &Connection) -> Result<()> {
23        let mut state = self.state.lock().unwrap();
24        
25        match *state {
26            TransactionState::Active => {
27                println!("Warning: Transaction already active. Use COMMIT or ROLLBACK first.");
28                return Ok(());
29            },
30            TransactionState::None => {
31                conn.execute("BEGIN", [])?;
32                *state = TransactionState::Active;
33                println!("Transaction started.");
34            }
35        }
36        
37        Ok(())
38    }
39
40    pub fn commit_transaction(&self, conn: &Connection) -> Result<()> {
41        let mut state = self.state.lock().unwrap();
42        
43        match *state {
44            TransactionState::None => {
45                println!("No active transaction to commit.");
46                return Ok(());
47            },
48            TransactionState::Active => {
49                conn.execute("COMMIT", [])?;
50                *state = TransactionState::None;
51                println!("Transaction committed.");
52            }
53        }
54        
55        Ok(())
56    }
57
58    pub fn rollback_transaction(&self, conn: &Connection) -> Result<()> {
59        let mut state = self.state.lock().unwrap();
60        
61        match *state {
62            TransactionState::None => {
63                println!("No active transaction to rollback.");
64                return Ok(());
65            },
66            TransactionState::Active => {
67                conn.execute("ROLLBACK", [])?;
68                *state = TransactionState::None;
69                println!("Transaction rolled back.");
70            }
71        }
72        
73        Ok(())
74    }
75
76    pub fn is_active(&self) -> bool {
77        matches!(*self.state.lock().unwrap(), TransactionState::Active)
78    }
79
80    pub fn show_status(&self) {
81        let state = self.state.lock().unwrap();
82        match *state {
83            TransactionState::None => println!("No active transaction."),
84            TransactionState::Active => println!("Transaction is active."),
85        }
86    }
87
88    // Handle commands that might affect transaction state
89    pub fn handle_sql_command(&self, conn: &Connection, sql: &str) -> Result<bool> {
90        let sql_lower = sql.to_lowercase().trim().to_string();
91        
92        match sql_lower.as_str() {
93            "begin" | "begin transaction" => {
94                self.begin_transaction(conn)?;
95                Ok(true) // Command was handled
96            },
97            "commit" | "commit transaction" => {
98                self.commit_transaction(conn)?;
99                Ok(true) // Command was handled
100            },
101            "rollback" | "rollback transaction" => {
102                self.rollback_transaction(conn)?;
103                Ok(true) // Command was handled
104            },
105            _ => {
106                // Handle DROP commands
107                if sql_lower.starts_with("drop") {
108                    let parts: Vec<&str> = sql_lower.split_whitespace().collect();
109                    if parts.len() < 2 {
110                        println!("Usage: DROP TABLE table_name; or DROP table_name;");
111                        return Ok(true);
112                    }
113                    
114                    let table_name = if parts[1] == "table" {
115                        if parts.len() < 3 {
116                            println!("Usage: DROP TABLE table_name;");
117                            return Ok(true);
118                        }
119                        parts[2].trim_end_matches(';')
120                    } else {
121                        parts[1].trim_end_matches(';')
122                    };
123                    
124                    // Verify table exists before dropping
125                    let mut stmt = conn.prepare("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?1")
126                        .context("Failed to prepare table existence check")?;
127                    
128                    let count: i64 = stmt.query_row(rusqlite::params![table_name], |row| row.get(0))
129                        .with_context(|| format!("Failed to check if table '{}' exists", table_name))?;
130                    
131                    if count == 0 {
132                        println!("Table '{}' does not exist", table_name);
133                        return Ok(true);
134                    }
135                    
136                    // Execute the DROP command
137                    conn.execute(&format!("DROP TABLE {}", table_name), [])
138                        .with_context(|| format!("Failed to drop table '{}'", table_name))?;
139                    
140                    println!("Table '{}' dropped successfully", table_name);
141                    return Ok(true);
142                }
143                Ok(false) // Command was not handled
144            }
145        }
146    }
147}