Skip to main content

mixtape_tools/sqlite/database/
close.rs

1//! Close database tool
2
3use crate::prelude::*;
4use crate::sqlite::error::SqliteToolError;
5use crate::sqlite::manager::DATABASE_MANAGER;
6
7/// Input for closing a database
8#[derive(Debug, Deserialize, JsonSchema)]
9pub struct CloseDatabaseInput {
10    /// Database file path to close. If not specified, closes the default database.
11    #[serde(default)]
12    pub db_path: Option<String>,
13}
14
15/// Tool for closing a SQLite database connection
16///
17/// Closes an open database connection and releases resources.
18/// If the closed database was the default, another open database
19/// becomes the new default (if any).
20pub struct CloseDatabaseTool;
21
22impl Tool for CloseDatabaseTool {
23    type Input = CloseDatabaseInput;
24
25    fn name(&self) -> &str {
26        "sqlite_close_database"
27    }
28
29    fn description(&self) -> &str {
30        "Close an open SQLite database connection. Specify the database name/path, or omit to close the default database."
31    }
32
33    async fn execute(&self, input: Self::Input) -> Result<ToolResult, ToolError> {
34        let db_name = input.db_path.clone();
35
36        let result = tokio::task::spawn_blocking(move || {
37            let name = match &db_name {
38                Some(n) => n.as_str(),
39                None => {
40                    DATABASE_MANAGER
41                        .get_default()
42                        .ok_or(SqliteToolError::NoDefaultDatabase)?
43                        .as_str()
44                        .to_string();
45                    // Get the default and close it
46                    let default = DATABASE_MANAGER
47                        .get_default()
48                        .ok_or(SqliteToolError::NoDefaultDatabase)?;
49                    return DATABASE_MANAGER.close(&default);
50                }
51            };
52            DATABASE_MANAGER.close(name)
53        })
54        .await
55        .map_err(|e| ToolError::Custom(format!("Task join error: {}", e)))?;
56
57        match result {
58            Ok(()) => {
59                let closed_name = input
60                    .db_path
61                    .unwrap_or_else(|| "default database".to_string());
62                let response = serde_json::json!({
63                    "status": "success",
64                    "message": format!("Database closed: {}", closed_name)
65                });
66                Ok(ToolResult::Json(response))
67            }
68            Err(e) => Err(e.into()),
69        }
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use crate::sqlite::test_utils::TestDatabase;
77
78    #[tokio::test]
79    async fn test_close_database() {
80        // Create an isolated test database
81        let db = TestDatabase::new().await;
82        let db_key = db.key();
83
84        // Close it explicitly by key
85        let close_tool = CloseDatabaseTool;
86        let close_input = CloseDatabaseInput {
87            db_path: Some(db_key.clone()),
88        };
89
90        let result = close_tool.execute(close_input).await;
91        assert!(result.is_ok());
92
93        // Verify it's closed
94        assert!(!DATABASE_MANAGER.is_open(&db_key));
95
96        // Prevent Drop from trying to close again
97        std::mem::forget(db);
98    }
99
100    #[test]
101    fn test_tool_metadata() {
102        let tool = CloseDatabaseTool;
103        assert_eq!(tool.name(), "sqlite_close_database");
104        assert!(!tool.description().is_empty());
105    }
106}