boost/tools/
migrations.rs1use async_trait::async_trait;
4use serde_json::{json, Value};
5
6use crate::protocol::CallToolResult;
7use crate::tool::{Context, Tool};
8
9pub struct ListMigrations;
10
11#[async_trait]
12impl Tool for ListMigrations {
13 fn name(&self) -> &'static str {
14 "list-migrations"
15 }
16 fn description(&self) -> &'static str {
17 "List migration files and whether each has been applied. Reads the `migrations` table."
18 }
19
20 async fn call(&self, ctx: &Context, _args: Value) -> CallToolResult {
21 let pool = ctx.container.driver_pool();
22 let driver = pool.driver();
23
24 let rows: Result<Vec<(String, Option<i64>, Option<chrono::DateTime<chrono::Utc>>)>, _> =
27 match pool {
28 cast_core::Pool::Postgres(p) => sqlx::query_as::<
29 _,
30 (String, Option<i64>, Option<chrono::DateTime<chrono::Utc>>),
31 >(
32 "SELECT name, batch, applied_at FROM migrations ORDER BY id",
33 )
34 .fetch_all(&p)
35 .await
36 .map_err(|e| e.to_string()),
37 cast_core::Pool::MySql(p) => sqlx::query_as::<
38 _,
39 (String, Option<i64>, Option<chrono::DateTime<chrono::Utc>>),
40 >(
41 "SELECT name, batch, applied_at FROM migrations ORDER BY id",
42 )
43 .fetch_all(&p)
44 .await
45 .map_err(|e| e.to_string()),
46 cast_core::Pool::Sqlite(p) => sqlx::query_as::<
47 _,
48 (String, Option<i64>, Option<chrono::DateTime<chrono::Utc>>),
49 >(
50 "SELECT name, batch, applied_at FROM migrations ORDER BY id",
51 )
52 .fetch_all(&p)
53 .await
54 .map_err(|e| e.to_string()),
55 };
56
57 let applied = match rows {
58 Ok(rows) => rows,
59 Err(e) => {
60 return CallToolResult::json(&json!({
61 "driver": format!("{:?}", driver),
62 "error": format!("could not read migrations table: {e}"),
63 "hint": "run `anvil migrate:install` to create the table, or `anvil migrate` to apply pending migrations.",
64 "applied": [],
65 }));
66 }
67 };
68
69 CallToolResult::json(&json!({
70 "driver": format!("{:?}", driver),
71 "count": applied.len(),
72 "applied": applied.iter().map(|(name, batch, ts)| {
73 json!({
74 "name": name,
75 "batch": batch,
76 "applied_at": ts.map(|t| t.to_rfc3339()),
77 })
78 }).collect::<Vec<_>>(),
79 }))
80 }
81}