spin_build/
manifest.rs

1use anyhow::Result;
2use serde::Deserialize;
3use std::{collections::BTreeMap, path::Path};
4
5use spin_manifest::{schema::v2, ManifestVersion};
6
7/// Returns a map of component IDs to [`v2::ComponentBuildConfig`]s for the
8/// given (v1 or v2) manifest path. If the manifest cannot be loaded, the
9/// function attempts fallback: if fallback succeeds, result is Ok but the load error
10/// is also returned via the second part of the return value tuple.
11pub async fn component_build_configs(
12    manifest_file: impl AsRef<Path>,
13) -> Result<(Vec<ComponentBuildInfo>, Option<spin_manifest::Error>)> {
14    let manifest = spin_manifest::manifest_from_file(&manifest_file);
15    match manifest {
16        Ok(manifest) => Ok((build_configs_from_manifest(manifest), None)),
17        Err(e) => fallback_load_build_configs(&manifest_file)
18            .await
19            .map(|bc| (bc, Some(e))),
20    }
21}
22
23fn build_configs_from_manifest(
24    mut manifest: spin_manifest::schema::v2::AppManifest,
25) -> Vec<ComponentBuildInfo> {
26    spin_manifest::normalize::normalize_manifest(&mut manifest);
27
28    manifest
29        .components
30        .into_iter()
31        .map(|(id, c)| ComponentBuildInfo {
32            id: id.to_string(),
33            build: c.build,
34        })
35        .collect()
36}
37
38async fn fallback_load_build_configs(
39    manifest_file: impl AsRef<Path>,
40) -> Result<Vec<ComponentBuildInfo>> {
41    let manifest_text = tokio::fs::read_to_string(manifest_file).await?;
42    Ok(match ManifestVersion::detect(&manifest_text)? {
43        ManifestVersion::V1 => {
44            let v1: ManifestV1BuildInfo = toml::from_str(&manifest_text)?;
45            v1.components
46        }
47        ManifestVersion::V2 => {
48            let v2: ManifestV2BuildInfo = toml::from_str(&manifest_text)?;
49            v2.components
50                .into_iter()
51                .map(|(id, mut c)| {
52                    c.id = id;
53                    c
54                })
55                .collect()
56        }
57    })
58}
59
60#[derive(Deserialize)]
61pub struct ComponentBuildInfo {
62    #[serde(default)]
63    pub id: String,
64    pub build: Option<v2::ComponentBuildConfig>,
65}
66
67#[derive(Deserialize)]
68struct ManifestV1BuildInfo {
69    #[serde(rename = "component")]
70    components: Vec<ComponentBuildInfo>,
71}
72
73#[derive(Deserialize)]
74struct ManifestV2BuildInfo {
75    #[serde(rename = "component")]
76    components: BTreeMap<String, ComponentBuildInfo>,
77}