1use crate::migrations::{self, Context, Steps};
7use crate::version::Version;
8use crate::{find_manifests, generate};
9use origin_manifest::Manifest;
10use std::path::Path;
11
12pub fn run(root: &Path, dry_run: bool) -> Result<(), String> {
14 let manifests = find_manifests(root)?;
15 if manifests.is_empty() {
16 return Err(format!("no app.toml found under {}", root.display()));
17 }
18
19 for manifest_path in manifests {
20 let manifest = Manifest::load(&manifest_path).map_err(|error| error.to_string())?;
21 let project = manifest_path.parent().unwrap_or(root);
22 let from = Version::parse(&manifest.origin.version)?;
23
24 println!("\n{}", manifest_path.display());
25 println!(" origin {from} → {}", migrations::CURRENT);
26
27 if from > migrations::CURRENT {
28 return Err(format!(
29 "{} tracks origin {from}, but this build only knows up to {}. \
30 Upgrade the origin dependency instead of downgrading the project.",
31 manifest_path.display(),
32 migrations::CURRENT
33 ));
34 }
35
36 if from == migrations::CURRENT {
37 println!(" already up to date");
38 continue;
39 }
40
41 let context = Context {
42 project,
43 manifest: &manifest,
44 dry_run,
45 };
46 let steps = migrations::apply(&context, from)?;
47
48 if !dry_run {
49 generate::run(root)?;
52 set_version(&manifest_path, migrations::CURRENT)?;
53 }
54
55 report(&steps, dry_run);
56 }
57
58 Ok(())
59}
60
61fn report(steps: &Steps, dry_run: bool) {
62 if dry_run {
63 println!("\n dry run — nothing was written");
64 }
65
66 for changed in &steps.changed {
67 println!(" ✓ {changed}");
68 }
69
70 for skipped in &steps.skipped {
71 println!(" – skipped: {skipped}");
72 }
73
74 if !steps.manual.is_empty() {
75 println!("\n Manual steps required:");
76 for manual in &steps.manual {
77 println!(" → {manual}");
78 }
79 }
80}
81
82fn set_version(manifest_path: &Path, version: Version) -> Result<(), String> {
87 let contents = std::fs::read_to_string(manifest_path)
88 .map_err(|error| format!("cannot read {}: {error}", manifest_path.display()))?;
89
90 let mut document: toml_edit::DocumentMut = contents
91 .parse()
92 .map_err(|error| format!("{} is not valid TOML: {error}", manifest_path.display()))?;
93
94 document["origin"]["version"] = toml_edit::value(version.to_string());
95
96 std::fs::write(manifest_path, document.to_string())
97 .map_err(|error| format!("cannot write {}: {error}", manifest_path.display()))
98}