Skip to main content

mixtape_tools/sqlite/transaction/
begin.rs

1//! Begin transaction tool
2
3use crate::prelude::*;
4use crate::sqlite::manager::with_connection;
5
6/// Input for beginning a transaction
7#[derive(Debug, Deserialize, JsonSchema)]
8pub struct BeginTransactionInput {
9    /// Database file path. If not specified, uses the default database.
10    #[serde(default)]
11    pub db_path: Option<String>,
12
13    /// Transaction type (default: DEFERRED)
14    #[serde(default)]
15    pub transaction_type: TransactionType,
16}
17
18/// SQLite transaction types
19#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)]
20#[serde(rename_all = "UPPERCASE")]
21pub enum TransactionType {
22    /// Deferred transaction (default) - locks acquired on first access
23    #[default]
24    Deferred,
25    /// Immediate transaction - acquires reserved lock immediately
26    Immediate,
27    /// Exclusive transaction - acquires exclusive lock immediately
28    Exclusive,
29}
30
31impl std::fmt::Display for TransactionType {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            TransactionType::Deferred => write!(f, "DEFERRED"),
35            TransactionType::Immediate => write!(f, "IMMEDIATE"),
36            TransactionType::Exclusive => write!(f, "EXCLUSIVE"),
37        }
38    }
39}
40
41/// Tool for beginning a database transaction
42///
43/// Starts a new transaction. All subsequent operations will be part of
44/// this transaction until committed or rolled back.
45pub struct BeginTransactionTool;
46
47impl Tool for BeginTransactionTool {
48    type Input = BeginTransactionInput;
49
50    fn name(&self) -> &str {
51        "sqlite_begin_transaction"
52    }
53
54    fn description(&self) -> &str {
55        "Begin a new database transaction. All subsequent operations will be part of this transaction until committed or rolled back."
56    }
57
58    async fn execute(&self, input: Self::Input) -> Result<ToolResult, ToolError> {
59        let tx_type = input.transaction_type;
60
61        with_connection(input.db_path, move |conn| {
62            let sql = format!("BEGIN {} TRANSACTION", tx_type);
63            conn.execute(&sql, [])?;
64            Ok(())
65        })
66        .await?;
67
68        let response = serde_json::json!({
69            "status": "success",
70            "message": format!("Transaction started ({})", tx_type)
71        });
72        Ok(ToolResult::Json(response))
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::sqlite::test_utils::TestDatabase;
80
81    #[tokio::test]
82    async fn test_begin_transaction() {
83        let db = TestDatabase::new().await;
84
85        let tool = BeginTransactionTool;
86        let input = BeginTransactionInput {
87            db_path: Some(db.key()),
88            transaction_type: TransactionType::Deferred,
89        };
90
91        let result = tool.execute(input).await;
92        assert!(result.is_ok());
93
94        // Rollback to clean up
95        db.execute("ROLLBACK");
96    }
97
98    #[test]
99    fn test_tool_metadata() {
100        let tool = BeginTransactionTool;
101        assert_eq!(tool.name(), "sqlite_begin_transaction");
102        assert!(!tool.description().is_empty());
103    }
104}