mixtape_tools/sqlite/table/
list.rs1use crate::prelude::*;
4use crate::sqlite::manager::with_connection;
5
6#[derive(Debug, Deserialize, JsonSchema)]
8pub struct ListTablesInput {
9 #[serde(default)]
11 pub db_path: Option<String>,
12}
13
14#[derive(Debug, Serialize, JsonSchema)]
16struct TableEntry {
17 name: String,
18 #[serde(rename = "type")]
19 table_type: String,
20}
21
22pub struct ListTablesTool;
28
29impl Tool for ListTablesTool {
30 type Input = ListTablesInput;
31
32 fn name(&self) -> &str {
33 "sqlite_list_tables"
34 }
35
36 fn description(&self) -> &str {
37 "List all tables and views in a SQLite database. Excludes SQLite internal tables (sqlite_*) \
38 and system tables managed by tools (_*). Returns the name and type of each table/view."
39 }
40
41 async fn execute(&self, input: Self::Input) -> Result<ToolResult, ToolError> {
42 let tables = with_connection(input.db_path, |conn| {
43 let mut stmt = conn.prepare(
44 "SELECT name, type FROM sqlite_master
45 WHERE type IN ('table', 'view')
46 AND name NOT LIKE 'sqlite_%'
47 AND name NOT LIKE '\\_%' ESCAPE '\\'
48 ORDER BY type, name",
49 )?;
50
51 let tables: Vec<TableEntry> = stmt
52 .query_map([], |row| {
53 Ok(TableEntry {
54 name: row.get(0)?,
55 table_type: row.get(1)?,
56 })
57 })?
58 .filter_map(|r| r.ok())
59 .collect();
60
61 Ok(tables)
62 })
63 .await?;
64
65 let count = tables.len();
66 Ok(ToolResult::Json(serde_json::json!({
67 "tables": tables,
68 "count": count
69 })))
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use crate::sqlite::test_utils::{unwrap_json, TestDatabase};
77
78 #[tokio::test]
79 async fn test_list_tables() {
80 let db = TestDatabase::with_schema(
81 "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);
82 CREATE TABLE posts (id INTEGER PRIMARY KEY, user_id INTEGER);
83 CREATE VIEW user_posts AS SELECT * FROM users JOIN posts ON users.id = posts.user_id;",
84 )
85 .await;
86
87 let result = ListTablesTool
88 .execute(ListTablesInput {
89 db_path: Some(db.key()),
90 })
91 .await
92 .unwrap();
93
94 let json = unwrap_json(result);
95 assert_eq!(json["count"].as_i64().unwrap(), 3);
96 }
97
98 #[test]
99 fn test_tool_metadata() {
100 let tool = ListTablesTool;
101 assert_eq!(tool.name(), "sqlite_list_tables");
102 assert!(!tool.description().is_empty());
103 }
104
105 #[tokio::test]
106 async fn test_excludes_system_tables() {
107 let db = TestDatabase::with_schema(
108 "CREATE TABLE users (id INTEGER PRIMARY KEY);
109 CREATE TABLE _system_table (id INTEGER PRIMARY KEY);",
110 )
111 .await;
112
113 let result = ListTablesTool
114 .execute(ListTablesInput {
115 db_path: Some(db.key()),
116 })
117 .await
118 .unwrap();
119
120 let json = unwrap_json(result);
121 assert_eq!(json["count"].as_i64().unwrap(), 1);
123 assert_eq!(json["tables"][0]["name"], "users");
124 }
125}