toasty_cli/migration/
apply.rs1use crate::Config;
2use anyhow::Result;
3use clap::Parser;
4use console::style;
5use hashbrown::HashSet;
6use std::fs;
7use toasty::Db;
8use toasty::migration::History;
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 = History::load_or_default(&history_path)?;
46
47 if history.entries().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
60 .driver()
61 .connect(&toasty::db::ConnectContext::default())
62 .await?;
63
64 let applied_migrations = conn.applied_migrations().await?;
66 let applied_ids: HashSet<u64> = applied_migrations.iter().map(|m| m.id()).collect();
67
68 let pending_migrations: Vec<_> = history
70 .entries()
71 .iter()
72 .filter(|m| !applied_ids.contains(&m.id))
73 .collect();
74
75 if pending_migrations.is_empty() {
76 println!(
77 " {}",
78 style("All migrations are already applied. Database is up to date.")
79 .green()
80 .dim()
81 );
82 println!();
83 return Ok(());
84 }
85
86 let pending_count = pending_migrations.len();
87 println!(
88 " {} Found {} pending migration(s) to apply",
89 style("→").cyan(),
90 pending_count
91 );
92 println!();
93
94 for migration_entry in &pending_migrations {
96 let migration_path = config
97 .migration
98 .get_migrations_dir()
99 .join(&migration_entry.name);
100
101 println!(
102 " {} Applying migration: {}",
103 style("→").cyan(),
104 style(&migration_entry.name).bold()
105 );
106
107 let sql = fs::read_to_string(&migration_path)?;
109 let migration = Migration::new_sql(sql);
110
111 conn.apply_migration(migration_entry.id, &migration_entry.name, &migration)
113 .await?;
114
115 println!(
116 " {} {}",
117 style("✓").green().bold(),
118 style(format!("Applied: {}", migration_entry.name)).dim()
119 );
120 }
121
122 println!();
123 println!(
124 " {}",
125 style(format!(
126 "Successfully applied {} migration(s)",
127 pending_count
128 ))
129 .green()
130 .bold()
131 );
132 println!();
133
134 Ok(())
135}