kaizen/core/
migrate_home.rs1use anyhow::Result;
5use std::path::Path;
6
7pub enum MigrationOutcome {
8 Skipped,
9 AlreadyMigrated,
10 Conflict,
11 Migrated,
12}
13
14pub fn migrate_legacy_in_repo(workspace: &Path, target: &Path) -> Result<MigrationOutcome> {
21 let old = workspace.join(".kaizen");
22 if !old.exists() {
23 return Ok(MigrationOutcome::Skipped);
24 }
25 let entries: Vec<_> = std::fs::read_dir(&old)?.filter_map(|e| e.ok()).collect();
26 if entries.len() == 1 && entries[0].file_name() == "MIGRATED.txt" {
27 return Ok(MigrationOutcome::AlreadyMigrated);
28 }
29 if target.exists() {
30 let n = std::fs::read_dir(target)?.filter_map(|e| e.ok()).count();
31 if n > 0 {
32 return Ok(MigrationOutcome::Conflict);
33 }
34 }
35 std::fs::create_dir_all(target)?;
36 for entry in &entries {
37 if entry.file_name() == "MIGRATED.txt" {
38 continue;
39 }
40 let src = entry.path();
41 let dst = target.join(entry.file_name());
42 if std::fs::rename(&src, &dst).is_err() {
43 copy_recursive(&src, &dst)?;
44 std::fs::remove_dir_all(&src).or_else(|_| std::fs::remove_file(&src))?;
45 }
46 }
47 write_marker(&old, target)?;
48 Ok(MigrationOutcome::Migrated)
49}
50
51fn write_marker(old: &Path, target: &Path) -> Result<()> {
52 let secs = std::time::SystemTime::now()
53 .duration_since(std::time::UNIX_EPOCH)
54 .map(|d| d.as_secs())
55 .unwrap_or(0);
56 std::fs::write(
57 old.join("MIGRATED.txt"),
58 format!(
59 "migrated to: {}\nat: {secs} (unix secs)\nsafe to delete this folder\n",
60 target.display()
61 ),
62 )?;
63 Ok(())
64}
65
66fn copy_recursive(src: &Path, dst: &Path) -> Result<()> {
67 if src.is_dir() {
68 std::fs::create_dir_all(dst)?;
69 for entry in std::fs::read_dir(src)?.filter_map(|e| e.ok()) {
70 copy_recursive(&entry.path(), &dst.join(entry.file_name()))?;
71 }
72 } else {
73 std::fs::copy(src, dst)?;
74 }
75 Ok(())
76}