Skip to main content

mixtape_tools/sqlite/database/
open.rs

1//! Open database tool
2
3use crate::prelude::*;
4use crate::sqlite::manager::DATABASE_MANAGER;
5use std::path::PathBuf;
6
7/// Input for opening a database
8#[derive(Debug, Deserialize, JsonSchema)]
9pub struct OpenDatabaseInput {
10    /// Database file path
11    pub db_path: PathBuf,
12
13    /// Whether to create the database if it doesn't exist (default: true)
14    #[serde(default = "default_create")]
15    pub create: bool,
16}
17
18fn default_create() -> bool {
19    true
20}
21
22/// Tool for opening or creating a SQLite database connection
23///
24/// This tool opens a database file and makes it available for subsequent operations.
25/// If `create` is true (default), the database will be created if it doesn't exist.
26/// The first opened database becomes the default for operations that don't specify one.
27pub struct OpenDatabaseTool;
28
29impl Tool for OpenDatabaseTool {
30    type Input = OpenDatabaseInput;
31
32    fn name(&self) -> &str {
33        "sqlite_open_database"
34    }
35
36    fn description(&self) -> &str {
37        "Open or create a SQLite database file. The database becomes available for subsequent operations. If create=true (default), creates the database if it doesn't exist."
38    }
39
40    async fn execute(&self, input: Self::Input) -> Result<ToolResult, ToolError> {
41        let result = tokio::task::spawn_blocking(move || {
42            DATABASE_MANAGER.open(&input.db_path, input.create)
43        })
44        .await
45        .map_err(|e| ToolError::Custom(format!("Task join error: {}", e)))?;
46
47        match result {
48            Ok(db_name) => {
49                let response = serde_json::json!({
50                    "status": "success",
51                    "database": db_name,
52                    "message": format!("Database opened successfully: {}", db_name)
53                });
54                Ok(ToolResult::Json(response))
55            }
56            Err(e) => Err(e.into()),
57        }
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use tempfile::TempDir;
65
66    #[tokio::test]
67    async fn test_open_database_create() {
68        let temp_dir = TempDir::new().unwrap();
69        let db_path = temp_dir.path().join("new_test.db");
70
71        let tool = OpenDatabaseTool;
72        let input = OpenDatabaseInput {
73            db_path: db_path.clone(),
74            create: true,
75        };
76
77        let result = tool.execute(input).await;
78        assert!(result.is_ok());
79
80        // Verify file was created
81        assert!(db_path.exists());
82
83        // Clean up only this database
84        let key = db_path
85            .canonicalize()
86            .unwrap()
87            .to_string_lossy()
88            .to_string();
89        let _ = DATABASE_MANAGER.close(&key);
90    }
91
92    #[tokio::test]
93    async fn test_open_database_no_create_missing() {
94        let temp_dir = TempDir::new().unwrap();
95        let db_path = temp_dir.path().join("nonexistent.db");
96
97        let tool = OpenDatabaseTool;
98        let input = OpenDatabaseInput {
99            db_path,
100            create: false,
101        };
102
103        let result = tool.execute(input).await;
104        assert!(result.is_err());
105        // No cleanup needed - database was never opened
106    }
107
108    #[test]
109    fn test_tool_metadata() {
110        let tool = OpenDatabaseTool;
111        assert_eq!(tool.name(), "sqlite_open_database");
112        assert!(!tool.description().is_empty());
113    }
114}