spawn_db/commands/migration/
adopt.rs1use crate::commands::migration::get_pending_and_confirm;
2use crate::commands::{Command, Outcome, TelemetryDescribe, TelemetryInfo};
3use crate::config::Config;
4use crate::engine::MigrationError;
5use anyhow::{anyhow, Result};
6use dialoguer::Editor;
7
8pub struct AdoptMigration {
9 pub migration: Option<String>,
10 pub yes: bool,
11 pub description: Option<String>,
12}
13
14impl TelemetryDescribe for AdoptMigration {
15 fn telemetry(&self) -> TelemetryInfo {
16 TelemetryInfo::new("migration adopt")
17 }
18}
19
20fn prompt_description() -> Result<String> {
23 let description = Editor::new()
24 .require_save(true)
25 .edit("# Why is this migration being adopted?\n# Lines starting with # will be ignored.\n")?
26 .map(|s| {
27 s.lines()
28 .filter(|line| !line.starts_with('#'))
29 .collect::<Vec<_>>()
30 .join("\n")
31 .trim()
32 .to_string()
33 })
34 .unwrap_or_default();
35
36 if description.is_empty() {
37 return Err(anyhow!("A description is required when adopting migrations. Use --description or provide one in the editor."));
38 }
39
40 Ok(description)
41}
42
43impl Command for AdoptMigration {
44 async fn execute(&self, config: &Config) -> Result<Outcome> {
45 let migrations = match &self.migration {
46 Some(migration) => vec![migration.clone()],
47 None => match get_pending_and_confirm(config, "adopt", self.yes).await? {
48 Some(pending) => pending,
49 None => return Ok(Outcome::AdoptedMigration),
50 },
51 };
52
53 let description = match &self.description {
54 Some(desc) => {
55 if desc.trim().is_empty() {
56 return Err(anyhow!(
57 "A description is required when adopting migrations."
58 ));
59 }
60 desc.clone()
61 }
62 None => prompt_description()?,
63 };
64
65 let engine = config.new_engine().await?;
66
67 let total = migrations.len();
68 for (i, migration) in migrations.iter().enumerate() {
69 let counter = if total > 1 {
70 format!(
71 "[{:>width$}/{}] ",
72 i + 1,
73 total,
74 width = total.to_string().len()
75 )
76 } else {
77 String::new()
78 };
79 match engine
80 .migration_adopt(migration, super::DEFAULT_NAMESPACE, &description)
81 .await
82 {
83 Ok(msg) => {
84 println!("{}{}", counter, msg);
85 }
86 Err(MigrationError::AlreadyApplied { info, .. }) => {
87 println!(
88 "{}Migration '{}' already applied (status: {}, activity: {})",
89 counter, migration, info.last_status, info.last_activity
90 );
91 }
92 Err(e) => {
93 return Err(
94 anyhow!(e).context(format!("Failed adopting migration '{}'", migration))
95 );
96 }
97 }
98 }
99
100 Ok(Outcome::AdoptedMigration)
101 }
102}