toasty_cli/migration/
apply.rs1use super::HistoryFile;
2use crate::Config;
3use anyhow::Result;
4use clap::Parser;
5use console::style;
6use std::collections::HashSet;
7use std::fs;
8use toasty::Db;
9use toasty::schema::db::Migration;
10
11#[derive(Parser, Debug)]
12pub struct ApplyCommand {}
13
14impl ApplyCommand {
15 pub(crate) async fn run(self, db: &Db, config: &Config) -> Result<()> {
16 println!();
17 println!(" {}", style("Apply Migrations").cyan().bold().underlined());
18 println!();
19 println!(
20 " {}",
21 style(format!(
22 "Connected to {}",
23 crate::utility::redact_url_password(&db.driver().url())
24 ))
25 .dim()
26 );
27 println!();
28
29 apply_migrations(db, config).await
30 }
31}
32
33pub(crate) async fn apply_migrations(db: &Db, config: &Config) -> Result<()> {
34 let history_path = config.migration.get_history_file_path();
35
36 let history = HistoryFile::load_or_default(&history_path)?;
38
39 if history.migrations().is_empty() {
40 println!(
41 " {}",
42 style("No migrations found in history file.")
43 .magenta()
44 .dim()
45 );
46 println!();
47 return Ok(());
48 }
49
50 let mut conn = db.driver().connect().await?;
52
53 let applied_migrations = conn.applied_migrations().await?;
55 let applied_ids: HashSet<u64> = applied_migrations.iter().map(|m| m.id()).collect();
56
57 let pending_migrations: Vec<_> = history
59 .migrations()
60 .iter()
61 .filter(|m| !applied_ids.contains(&m.id))
62 .collect();
63
64 if pending_migrations.is_empty() {
65 println!(
66 " {}",
67 style("All migrations are already applied. Database is up to date.")
68 .green()
69 .dim()
70 );
71 println!();
72 return Ok(());
73 }
74
75 let pending_count = pending_migrations.len();
76 println!(
77 " {} Found {} pending migration(s) to apply",
78 style("→").cyan(),
79 pending_count
80 );
81 println!();
82
83 for migration_entry in &pending_migrations {
85 let migration_path = config
86 .migration
87 .get_migrations_dir()
88 .join(&migration_entry.name);
89
90 println!(
91 " {} Applying migration: {}",
92 style("→").cyan(),
93 style(&migration_entry.name).bold()
94 );
95
96 let sql = fs::read_to_string(&migration_path)?;
98 let migration = Migration::new_sql(sql);
99
100 conn.apply_migration(migration_entry.id, migration_entry.name.clone(), &migration)
102 .await?;
103
104 println!(
105 " {} {}",
106 style("✓").green().bold(),
107 style(format!("Applied: {}", migration_entry.name)).dim()
108 );
109 }
110
111 println!();
112 println!(
113 " {}",
114 style(format!(
115 "Successfully applied {} migration(s)",
116 pending_count
117 ))
118 .green()
119 .bold()
120 );
121 println!();
122
123 Ok(())
124}