spin_doctor/
manifest.rs

1use std::fs;
2
3use anyhow::{Context, Result};
4use async_trait::async_trait;
5use spin_common::ui::quoted_path;
6use toml_edit::DocumentMut;
7
8use crate::Treatment;
9
10/// Diagnose app manifest trigger config problems.
11pub mod trigger;
12/// Diagnose old app manifest versions.
13pub mod upgrade;
14/// Diagnose upgradable app manifest versions.
15pub mod version;
16
17/// ManifestTreatment helps implement [`Treatment`]s for app manifest problems.
18#[async_trait]
19pub trait ManifestTreatment {
20    /// Return a short (single line) description of what this fix will do, as
21    /// an imperative, e.g. "Add default trigger config".
22    fn summary(&self) -> String;
23
24    /// Attempt to fix this problem. See [`Treatment::treat`].
25    async fn treat_manifest(&self, doc: &mut DocumentMut) -> Result<()>;
26}
27
28#[async_trait]
29impl<T: ManifestTreatment + Sync> Treatment for T {
30    fn summary(&self) -> String {
31        ManifestTreatment::summary(self)
32    }
33
34    async fn dry_run(&self, patient: &crate::PatientApp) -> Result<String> {
35        let mut after_doc = patient.manifest_doc.clone();
36        self.treat_manifest(&mut after_doc).await?;
37        let before = patient.manifest_doc.to_string();
38        let after = after_doc.to_string();
39        let diff = similar::udiff::unified_diff(Default::default(), &before, &after, 1, None);
40        Ok(format!(
41            "Apply the following diff to {}:\n{}",
42            quoted_path(&patient.manifest_path),
43            diff
44        ))
45    }
46
47    async fn treat(&self, patient: &mut crate::PatientApp) -> Result<()> {
48        let doc = &mut patient.manifest_doc;
49        self.treat_manifest(doc).await?;
50        let path = &patient.manifest_path;
51        fs::write(path, doc.to_string())
52            .with_context(|| format!("failed to write fixed manifest to {}", quoted_path(path)))
53    }
54}