oxicode/cli/commands/
migrate.rs1use std::path::PathBuf;
27
28use crate::cli::{MigrateBrainArgs, MigrationCommands};
29
30pub fn handle_migrate(cmd: MigrationCommands) -> i32 {
32 match cmd {
33 MigrationCommands::Brain(args) => handle_migrate_brain(args),
34 }
35}
36
37fn handle_migrate_brain(args: MigrateBrainArgs) -> i32 {
38 let socket = args
39 .socket
40 .unwrap_or_else(crate::foundation::brain::default_socket_path);
41
42 println!("Foundation v1 brain migration");
43 println!("-----------------------------");
44 println!("socket: {}", socket.display());
45 println!("dry-run: {}", args.dry_run);
46 println!("archive-legacy: {}", args.archive_legacy);
47 println!("checkpoint: {}", args.checkpoint.display());
48 println!("batch size: {}", args.batch_size);
49
50 if args.dry_run {
51 println!("\nDRY RUN: no memory will be written, no checkpoint advanced.");
52 println!("The legacy store will be enumerated but not read for content.");
53 }
54
55 let backend = crate::foundation::brain::BrainMemoryBackend::new(socket.clone());
56 println!("\nbackend health: {}", backend.health().info());
57
58 if args.dry_run {
59 println!("\nDry run complete. Re-run without --dry-run to perform the migration.");
60 return 0;
61 }
62
63 let checkpoint = crate::foundation::migrate::Checkpoint::load(&args.checkpoint);
64 if let Some(last) = checkpoint.last_id() {
65 println!("resuming after id: {last}");
66 } else {
67 println!("starting fresh (no checkpoint found)");
68 }
69
70 let legacy = crate::foundation::migrate::LegacyMemoryReader::for_default_home();
71 let mut migrated = 0usize;
72 let mut failed = 0usize;
73 let mut tx = crate::foundation::migrate::Migration::new(&backend, &args.checkpoint);
74
75 for batch in legacy.batches(args.batch_size) {
76 for item in batch {
77 match tx.migrate_one(item) {
78 Ok(crate::foundation::migrate::MigrationOutcome::Inserted(id)) => {
79 println!(" + {id}");
80 migrated += 1;
81 }
82 Ok(crate::foundation::migrate::MigrationOutcome::Skipped(id)) => {
83 println!(" ~ {id} (already in brain)");
84 }
85 Err(e) => {
86 println!(" ! {e}");
87 failed += 1;
88 }
89 }
90 }
91 }
92
93 println!("\nMigration summary");
94 println!(" inserted: {migrated}");
95 println!(" failed: {failed}");
96
97 if args.archive_legacy {
98 match crate::foundation::migrate::archive_legacy_default() {
99 Ok(archive_path) => {
100 println!("\nLegacy store archived to: {}", archive_path.display());
101 }
102 Err(e) => {
103 println!("\nArchive failed: {e}");
104 return 2;
105 }
106 }
107 }
108
109 if failed > 0 { 1 } else { 0 }
110}
111
112impl Default for MigrateBrainArgs {
113 fn default() -> Self {
114 Self {
115 socket: None,
116 dry_run: false,
117 archive_legacy: false,
118 checkpoint: PathBuf::from("~/.oxicode/migration/brain.json"),
119 batch_size: 64,
120 }
121 }
122}