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)]
20pub struct ApplyCommand {}
21
22impl ApplyCommand {
23 pub(crate) async fn run(self, db: &Db, config: &Config) -> Result<()> {
24 println!();
25 println!(" {}", style("Apply Migrations").cyan().bold().underlined());
26 println!();
27 println!(
28 " {}",
29 style(format!(
30 "Connected to {}",
31 crate::utility::redact_url_password(&db.driver().url())
32 ))
33 .dim()
34 );
35 println!();
36
37 apply_migrations(db, config).await
38 }
39}
40
41pub(crate) async fn apply_migrations(db: &Db, config: &Config) -> Result<()> {
42 let history_path = config.migration.get_history_file_path();
43
44 let history = HistoryFile::load_or_default(&history_path)?;
46
47 if history.migrations().is_empty() {
48 println!(
49 " {}",
50 style("No migrations found in history file.")
51 .magenta()
52 .dim()
53 );
54 println!();
55 return Ok(());
56 }
57
58 let mut conn = db.driver().connect().await?;
60
61 let applied_migrations = conn.applied_migrations().await?;
63 let applied_ids: HashSet<u64> = applied_migrations.iter().map(|m| m.id()).collect();
64
65 let pending_migrations: Vec<_> = history
67 .migrations()
68 .iter()
69 .filter(|m| !applied_ids.contains(&m.id))
70 .collect();
71
72 if pending_migrations.is_empty() {
73 println!(
74 " {}",
75 style("All migrations are already applied. Database is up to date.")
76 .green()
77 .dim()
78 );
79 println!();
80 return Ok(());
81 }
82
83 let pending_count = pending_migrations.len();
84 println!(
85 " {} Found {} pending migration(s) to apply",
86 style("→").cyan(),
87 pending_count
88 );
89 println!();
90
91 for migration_entry in &pending_migrations {
93 let migration_path = config
94 .migration
95 .get_migrations_dir()
96 .join(&migration_entry.name);
97
98 println!(
99 " {} Applying migration: {}",
100 style("→").cyan(),
101 style(&migration_entry.name).bold()
102 );
103
104 let sql = fs::read_to_string(&migration_path)?;
106 let migration = Migration::new_sql(sql);
107
108 conn.apply_migration(migration_entry.id, &migration_entry.name, &migration)
110 .await?;
111
112 println!(
113 " {} {}",
114 style("✓").green().bold(),
115 style(format!("Applied: {}", migration_entry.name)).dim()
116 );
117 }
118
119 println!();
120 println!(
121 " {}",
122 style(format!(
123 "Successfully applied {} migration(s)",
124 pending_count
125 ))
126 .green()
127 .bold()
128 );
129 println!();
130
131 Ok(())
132}