vapor_cli/
transactions.rs1use 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 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) },
97 "commit" | "commit transaction" => {
98 self.commit_transaction(conn)?;
99 Ok(true) },
101 "rollback" | "rollback transaction" => {
102 self.rollback_transaction(conn)?;
103 Ok(true) },
105 _ => {
106 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 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 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) }
145 }
146 }
147}