ralph/migration/file_migrations/
rename.rs1use anyhow::{Context, Result};
22use std::fs;
23
24use super::super::MigrationContext;
25use super::config_refs::update_config_file_references;
26
27#[derive(Debug, Clone)]
29pub struct FileMigrationOptions {
30 pub keep_backup: bool,
32 pub update_config: bool,
34}
35
36impl Default for FileMigrationOptions {
37 fn default() -> Self {
38 Self {
39 keep_backup: true,
40 update_config: true,
41 }
42 }
43}
44
45pub fn apply_file_rename(ctx: &MigrationContext, old_path: &str, new_path: &str) -> Result<()> {
48 let opts = FileMigrationOptions::default();
49 apply_file_rename_with_options(ctx, old_path, new_path, &opts)
50}
51
52pub fn apply_file_rename_with_options(
54 ctx: &MigrationContext,
55 old_path: &str,
56 new_path: &str,
57 opts: &FileMigrationOptions,
58) -> Result<()> {
59 let old_full_path = ctx.resolve_path(old_path);
60 let new_full_path = ctx.resolve_path(new_path);
61
62 if !old_full_path.exists() {
63 anyhow::bail!("Source file does not exist: {}", old_full_path.display());
64 }
65
66 if new_full_path.exists() && old_full_path != new_full_path {
67 anyhow::bail!(
68 "Destination file already exists: {}",
69 new_full_path.display()
70 );
71 }
72
73 if let Some(parent) = new_full_path.parent() {
74 fs::create_dir_all(parent)
75 .with_context(|| format!("create parent directory {}", parent.display()))?;
76 }
77
78 fs::copy(&old_full_path, &new_full_path).with_context(|| {
79 format!(
80 "copy {} to {}",
81 old_full_path.display(),
82 new_full_path.display()
83 )
84 })?;
85
86 log::info!(
87 "Migrated file from {} to {}",
88 old_full_path.display(),
89 new_full_path.display()
90 );
91
92 if opts.update_config {
93 update_config_file_references(ctx, old_path, new_path)
94 .context("update config file references")?;
95 }
96
97 if !opts.keep_backup {
98 fs::remove_file(&old_full_path)
99 .with_context(|| format!("remove original file {}", old_full_path.display()))?;
100 log::debug!("Removed original file {}", old_full_path.display());
101 } else {
102 log::debug!("Kept original file {} as backup", old_full_path.display());
103 }
104
105 Ok(())
106}
107
108pub fn rollback_file_migration(
111 ctx: &MigrationContext,
112 old_path: &str,
113 new_path: &str,
114) -> Result<()> {
115 let old_full_path = ctx.resolve_path(old_path);
116 let new_full_path = ctx.resolve_path(new_path);
117
118 if !old_full_path.exists() {
119 anyhow::bail!(
120 "Cannot rollback: original file {} does not exist",
121 old_full_path.display()
122 );
123 }
124
125 if new_full_path.exists() {
126 fs::remove_file(&new_full_path)
127 .with_context(|| format!("remove migrated file {}", new_full_path.display()))?;
128 }
129
130 log::info!(
131 "Rolled back file migration: restored {}, removed {}",
132 old_full_path.display(),
133 new_full_path.display()
134 );
135
136 Ok(())
137}