spin_manifest/schema/
common.rs

1use std::fmt::Display;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use wasm_pkg_common::{package::PackageRef, registry::Registry};
7
8/// Variable definition
9#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
10#[serde(deny_unknown_fields)]
11pub struct Variable {
12    /// `required = true`
13    #[serde(default, skip_serializing_if = "is_false")]
14    pub required: bool,
15    /// `default = "default value"`
16    #[serde(default, skip_serializing_if = "Option::is_none")]
17    pub default: Option<String>,
18    /// `secret = true`
19    #[serde(default, skip_serializing_if = "is_false")]
20    pub secret: bool,
21}
22
23/// Component source
24#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
25#[serde(deny_unknown_fields, untagged)]
26pub enum ComponentSource {
27    /// `"local.wasm"`
28    Local(String),
29    /// `{ ... }`
30    Remote {
31        /// `url = "https://example.test/remote.wasm"`
32        url: String,
33        /// `digest = `"sha256:abc123..."`
34        digest: String,
35    },
36    /// `{ ... }`
37    Registry {
38        /// `registry = "example.com"`
39        #[schemars(with = "Option<String>")]
40        registry: Option<Registry>,
41        /// `package = "example:component"`
42        #[schemars(with = "String")]
43        package: PackageRef,
44        /// `version = "1.2.3"`
45        version: String,
46    },
47}
48
49impl Display for ComponentSource {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        match self {
52            ComponentSource::Local(path) => write!(f, "{path:?}"),
53            ComponentSource::Remote { url, digest } => write!(f, "{url:?} with digest {digest:?}"),
54            ComponentSource::Registry {
55                registry,
56                package,
57                version,
58            } => {
59                let registry_suffix = match registry {
60                    None => "default registry".to_owned(),
61                    Some(r) => format!("registry {r:?}"),
62                };
63                write!(f, "\"{package}@{version}\" from {registry_suffix}")
64            }
65        }
66    }
67}
68
69/// WASI files mount
70#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
71#[serde(deny_unknown_fields, untagged)]
72pub enum WasiFilesMount {
73    /// `"images/*.png"`
74    Pattern(String),
75    /// `{ ... }`
76    Placement {
77        /// `source = "content/dir"`
78        source: String,
79        /// `destination = "/"`
80        destination: String,
81    },
82}
83
84/// Component build configuration
85#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
86#[serde(deny_unknown_fields)]
87pub struct ComponentBuildConfig {
88    /// `command = "cargo build"`
89    pub command: Commands,
90    /// `workdir = "components/main"
91    #[serde(default, skip_serializing_if = "Option::is_none")]
92    pub workdir: Option<String>,
93    /// watch = ["src/**/*.rs"]
94    #[serde(default, skip_serializing_if = "Vec::is_empty")]
95    pub watch: Vec<String>,
96}
97
98impl ComponentBuildConfig {
99    /// The commands to execute for the build
100    pub fn commands(&self) -> impl ExactSizeIterator<Item = &String> {
101        let as_vec = match &self.command {
102            Commands::Single(cmd) => vec![cmd],
103            Commands::Multiple(cmds) => cmds.iter().collect(),
104        };
105        as_vec.into_iter()
106    }
107}
108
109/// Component build command or commands
110#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
111#[serde(untagged)]
112pub enum Commands {
113    /// `command = "cargo build"`
114    Single(String),
115    /// `command = ["cargo build", "wac encode compose-deps.wac -d my:pkg=app.wasm --registry fermyon.com"]`
116    Multiple(Vec<String>),
117}
118
119fn is_false(v: &bool) -> bool {
120    !*v
121}