grex_cli/cli/verbs/
migrate_lockfile.rs1use crate::cli::args::{GlobalFlags, MigrateLockfileArgs};
26use anyhow::Result;
27use grex_core::lockfile::migrate_v1_1_1::migrate_v1_1_1_lockfile;
28use grex_core::lockfile::{detect_legacy_lockfile, meta_lockfile_path};
29use std::path::Path;
30use tokio_util::sync::CancellationToken;
31
32pub fn run(
33 args: MigrateLockfileArgs,
34 global: &GlobalFlags,
35 _cancel: &CancellationToken,
36) -> Result<()> {
37 let workspace = match args.workspace.clone() {
38 Some(p) => p,
39 None => std::env::current_dir()?,
40 };
41 let dry_run = args.dry_run || global.dry_run;
42
43 if dry_run {
44 run_dry_run(&workspace, global.json)
45 } else {
46 run_migrate(&workspace, global.json)
47 }
48}
49
50fn run_dry_run(workspace: &std::path::Path, json: bool) -> Result<()> {
55 let lockfile = meta_lockfile_path(workspace);
56 if !lockfile.exists() {
57 emit_outcome(json, &lockfile, Outcome::NoLockfile, true);
58 return Ok(());
59 }
60 let legacy = detect_legacy_lockfile(workspace)?;
61 let outcome = if legacy { Outcome::WouldMigrate } else { Outcome::AlreadyMigrated };
62 emit_outcome(json, &lockfile, outcome, true);
63 Ok(())
64}
65
66fn run_migrate(workspace: &std::path::Path, json: bool) -> Result<()> {
69 let report = migrate_v1_1_1_lockfile(workspace)?;
70 let outcome = if report.no_lockfile {
71 Outcome::NoLockfile
72 } else if report.already_migrated {
73 Outcome::AlreadyMigrated
74 } else {
75 Outcome::Migrated { entries: report.migrated_entries }
76 };
77 emit_outcome(json, &report.lockfile, outcome, false);
78 Ok(())
79}
80
81#[derive(Debug, Clone, Copy)]
82enum Outcome {
83 NoLockfile,
85 AlreadyMigrated,
87 WouldMigrate,
89 Migrated { entries: usize },
91}
92
93fn emit_outcome(json: bool, lockfile: &Path, outcome: Outcome, dry_run: bool) {
94 if json {
95 let (status, entries) = match outcome {
96 Outcome::NoLockfile => ("no_lockfile", None),
97 Outcome::AlreadyMigrated => ("already_migrated", None),
98 Outcome::WouldMigrate => ("would_migrate", None),
99 Outcome::Migrated { entries } => ("migrated", Some(entries)),
100 };
101 let doc = serde_json::json!({
102 "verb": "migrate-lockfile",
103 "lockfile": lockfile.display().to_string(),
104 "status": status,
105 "entries": entries,
106 "dry_run": dry_run,
107 });
108 if let Ok(s) = serde_json::to_string(&doc) {
109 println!("{s}");
110 }
111 } else {
112 let path = lockfile.display();
113 match outcome {
114 Outcome::NoLockfile => {
115 println!("no lockfile at {path}; nothing to migrate");
116 }
117 Outcome::AlreadyMigrated => {
118 println!("{path}: already on v1.2.0 schema; no-op");
119 }
120 Outcome::WouldMigrate => {
121 println!("{path}: v1.1.x → v1.2.0 (would migrate; --dry-run, no write)");
122 }
123 Outcome::Migrated { entries } => {
124 println!("{path}: migrated {entries} entries v1.1.x → v1.2.0");
125 }
126 }
127 }
128}