use crate::prelude::*;
use crate::sqlite::manager::with_connection;
use crate::sqlite::types::DatabaseInfo;
use std::path::Path;
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DatabaseInfoInput {
#[serde(default)]
pub db_path: Option<String>,
}
pub struct DatabaseInfoTool;
impl Tool for DatabaseInfoTool {
type Input = DatabaseInfoInput;
fn name(&self) -> &str {
"sqlite_database_info"
}
fn description(&self) -> &str {
"Get comprehensive metadata and statistics about a SQLite database including file size, table counts, indexes, and configuration."
}
async fn execute(&self, input: Self::Input) -> Result<ToolResult, ToolError> {
let result = with_connection(input.db_path, |conn| {
let path: String = conn
.query_row("PRAGMA database_list", [], |row| row.get(2))
.unwrap_or_else(|_| "unknown".to_string());
let size_bytes = Path::new(&path)
.metadata()
.map(|m| m.len())
.unwrap_or(0);
let table_count: usize = conn
.query_row(
"SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'",
[],
|row| row.get::<_, i64>(0).map(|v| v as usize),
)
.unwrap_or(0);
let index_count: usize = conn
.query_row(
"SELECT COUNT(*) FROM sqlite_master WHERE type = 'index'",
[],
|row| row.get::<_, i64>(0).map(|v| v as usize),
)
.unwrap_or(0);
let view_count: usize = conn
.query_row(
"SELECT COUNT(*) FROM sqlite_master WHERE type = 'view'",
[],
|row| row.get::<_, i64>(0).map(|v| v as usize),
)
.unwrap_or(0);
let trigger_count: usize = conn
.query_row(
"SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger'",
[],
|row| row.get::<_, i64>(0).map(|v| v as usize),
)
.unwrap_or(0);
let sqlite_version: String = conn
.query_row("SELECT sqlite_version()", [], |row| row.get(0))
.unwrap_or_else(|_| "unknown".to_string());
let page_size: i64 = conn
.query_row("PRAGMA page_size", [], |row| row.get(0))
.unwrap_or(0);
let page_count: i64 = conn
.query_row("PRAGMA page_count", [], |row| row.get(0))
.unwrap_or(0);
let journal_mode: String = conn
.query_row("PRAGMA journal_mode", [], |row| row.get(0))
.unwrap_or_else(|_| "delete".to_string());
let wal_mode = journal_mode.to_lowercase() == "wal";
Ok(DatabaseInfo {
path,
size_bytes,
table_count,
index_count,
view_count,
trigger_count,
sqlite_version,
page_size,
page_count,
wal_mode,
})
})
.await?;
Ok(ToolResult::Json(serde_json::to_value(result)?))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sqlite::test_utils::{unwrap_json, TestDatabase};
#[tokio::test]
async fn test_database_info() {
let db =
TestDatabase::with_schema("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT);")
.await;
let info_tool = DatabaseInfoTool;
let info_input = DatabaseInfoInput {
db_path: Some(db.key()),
};
let result = info_tool.execute(info_input).await.unwrap();
let json = unwrap_json(result);
assert_eq!(json["table_count"].as_i64().unwrap(), 1);
assert!(json["sqlite_version"].as_str().is_some());
}
#[test]
fn test_tool_metadata() {
let tool = DatabaseInfoTool;
assert_eq!(tool.name(), "sqlite_database_info");
assert!(!tool.description().is_empty());
}
}