mixtape_tools/sqlite/database/
info.rs1use crate::prelude::*;
4use crate::sqlite::manager::with_connection;
5use crate::sqlite::types::DatabaseInfo;
6use std::path::Path;
7
8#[derive(Debug, Deserialize, JsonSchema)]
10pub struct DatabaseInfoInput {
11 #[serde(default)]
13 pub db_path: Option<String>,
14}
15
16pub struct DatabaseInfoTool;
23
24impl Tool for DatabaseInfoTool {
25 type Input = DatabaseInfoInput;
26
27 fn name(&self) -> &str {
28 "sqlite_database_info"
29 }
30
31 fn description(&self) -> &str {
32 "Get comprehensive metadata and statistics about a SQLite database including file size, table counts, indexes, and configuration."
33 }
34
35 async fn execute(&self, input: Self::Input) -> Result<ToolResult, ToolError> {
36 let result = with_connection(input.db_path, |conn| {
37 let path: String = conn
39 .query_row("PRAGMA database_list", [], |row| row.get(2))
40 .unwrap_or_else(|_| "unknown".to_string());
41
42 let size_bytes = Path::new(&path)
44 .metadata()
45 .map(|m| m.len())
46 .unwrap_or(0);
47
48 let table_count: usize = conn
50 .query_row(
51 "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'",
52 [],
53 |row| row.get::<_, i64>(0).map(|v| v as usize),
54 )
55 .unwrap_or(0);
56
57 let index_count: usize = conn
59 .query_row(
60 "SELECT COUNT(*) FROM sqlite_master WHERE type = 'index'",
61 [],
62 |row| row.get::<_, i64>(0).map(|v| v as usize),
63 )
64 .unwrap_or(0);
65
66 let view_count: usize = conn
68 .query_row(
69 "SELECT COUNT(*) FROM sqlite_master WHERE type = 'view'",
70 [],
71 |row| row.get::<_, i64>(0).map(|v| v as usize),
72 )
73 .unwrap_or(0);
74
75 let trigger_count: usize = conn
77 .query_row(
78 "SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger'",
79 [],
80 |row| row.get::<_, i64>(0).map(|v| v as usize),
81 )
82 .unwrap_or(0);
83
84 let sqlite_version: String = conn
86 .query_row("SELECT sqlite_version()", [], |row| row.get(0))
87 .unwrap_or_else(|_| "unknown".to_string());
88
89 let page_size: i64 = conn
91 .query_row("PRAGMA page_size", [], |row| row.get(0))
92 .unwrap_or(0);
93
94 let page_count: i64 = conn
96 .query_row("PRAGMA page_count", [], |row| row.get(0))
97 .unwrap_or(0);
98
99 let journal_mode: String = conn
101 .query_row("PRAGMA journal_mode", [], |row| row.get(0))
102 .unwrap_or_else(|_| "delete".to_string());
103 let wal_mode = journal_mode.to_lowercase() == "wal";
104
105 Ok(DatabaseInfo {
106 path,
107 size_bytes,
108 table_count,
109 index_count,
110 view_count,
111 trigger_count,
112 sqlite_version,
113 page_size,
114 page_count,
115 wal_mode,
116 })
117 })
118 .await?;
119
120 Ok(ToolResult::Json(serde_json::to_value(result)?))
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use crate::sqlite::test_utils::{unwrap_json, TestDatabase};
128
129 #[tokio::test]
130 async fn test_database_info() {
131 let db =
132 TestDatabase::with_schema("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT);")
133 .await;
134
135 let info_tool = DatabaseInfoTool;
137 let info_input = DatabaseInfoInput {
138 db_path: Some(db.key()),
139 };
140
141 let result = info_tool.execute(info_input).await.unwrap();
142 let json = unwrap_json(result);
143
144 assert_eq!(json["table_count"].as_i64().unwrap(), 1);
145 assert!(json["sqlite_version"].as_str().is_some());
146 }
147
148 #[test]
149 fn test_tool_metadata() {
150 let tool = DatabaseInfoTool;
151 assert_eq!(tool.name(), "sqlite_database_info");
152 assert!(!tool.description().is_empty());
153 }
154}