1use alloc::string::String;
4use alloc::vec::Vec;
5
6use semver::Version;
7
8use crate::flow::FlowKind;
9use crate::{ComponentId, FlowId};
10
11#[cfg(feature = "schemars")]
12use schemars::JsonSchema;
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16#[derive(Clone, Debug, PartialEq, Eq)]
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19#[cfg_attr(feature = "schemars", derive(JsonSchema))]
20pub struct ComponentManifest {
21 pub id: ComponentId,
23 #[cfg_attr(
25 feature = "schemars",
26 schemars(with = "String", description = "SemVer version")
27 )]
28 pub version: Version,
29 #[cfg_attr(feature = "serde", serde(default))]
31 pub supports: Vec<FlowKind>,
32 pub world: String,
34 pub profiles: ComponentProfiles,
36 pub capabilities: ComponentCapabilities,
38 #[cfg_attr(
40 feature = "serde",
41 serde(default, skip_serializing_if = "Option::is_none")
42 )]
43 pub configurators: Option<ComponentConfigurators>,
44}
45
46impl ComponentManifest {
47 pub fn supports_kind(&self, kind: FlowKind) -> bool {
49 self.supports.iter().copied().any(|entry| entry == kind)
50 }
51
52 pub fn select_profile<'a>(
55 &'a self,
56 requested: Option<&str>,
57 ) -> Result<Option<&'a str>, ComponentProfileError> {
58 if let Some(name) = requested {
59 let matched = self
60 .profiles
61 .supported
62 .iter()
63 .find(|candidate| candidate.as_str() == name)
64 .ok_or_else(|| ComponentProfileError::UnsupportedProfile {
65 requested: name.to_owned(),
66 supported: self.profiles.supported.clone(),
67 })?;
68 Ok(Some(matched.as_str()))
69 } else {
70 Ok(self.profiles.default.as_deref())
71 }
72 }
73
74 pub fn basic_configurator(&self) -> Option<&FlowId> {
76 self.configurators
77 .as_ref()
78 .and_then(|cfg| cfg.basic.as_ref())
79 }
80
81 pub fn full_configurator(&self) -> Option<&FlowId> {
83 self.configurators
84 .as_ref()
85 .and_then(|cfg| cfg.full.as_ref())
86 }
87}
88
89#[derive(Clone, Debug, PartialEq, Eq, Default)]
91#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
92#[cfg_attr(feature = "schemars", derive(JsonSchema))]
93pub struct ComponentProfiles {
94 #[cfg_attr(
96 feature = "serde",
97 serde(default, skip_serializing_if = "Option::is_none")
98 )]
99 pub default: Option<String>,
100 #[cfg_attr(feature = "serde", serde(default))]
102 pub supported: Vec<String>,
103}
104
105#[derive(Clone, Debug, PartialEq, Eq)]
107#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
108#[cfg_attr(feature = "schemars", derive(JsonSchema))]
109pub struct ComponentConfigurators {
110 #[cfg_attr(
112 feature = "serde",
113 serde(default, skip_serializing_if = "Option::is_none")
114 )]
115 pub basic: Option<FlowId>,
116 #[cfg_attr(
118 feature = "serde",
119 serde(default, skip_serializing_if = "Option::is_none")
120 )]
121 pub full: Option<FlowId>,
122}
123
124#[derive(Clone, Debug, PartialEq, Eq, Default)]
126#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
127#[cfg_attr(feature = "schemars", derive(JsonSchema))]
128pub struct ComponentCapabilities {
129 pub wasi: WasiCapabilities,
131 pub host: HostCapabilities,
133}
134
135#[derive(Clone, Debug, PartialEq, Eq, Default)]
137#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
138#[cfg_attr(feature = "schemars", derive(JsonSchema))]
139pub struct WasiCapabilities {
140 #[cfg_attr(
142 feature = "serde",
143 serde(default, skip_serializing_if = "Option::is_none")
144 )]
145 pub filesystem: Option<FilesystemCapabilities>,
146 #[cfg_attr(
148 feature = "serde",
149 serde(default, skip_serializing_if = "Option::is_none")
150 )]
151 pub env: Option<EnvCapabilities>,
152 #[cfg_attr(feature = "serde", serde(default))]
154 pub random: bool,
155 #[cfg_attr(feature = "serde", serde(default))]
157 pub clocks: bool,
158}
159
160#[derive(Clone, Debug, PartialEq, Eq)]
162#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
163#[cfg_attr(feature = "schemars", derive(JsonSchema))]
164pub struct FilesystemCapabilities {
165 pub mode: FilesystemMode,
167 #[cfg_attr(feature = "serde", serde(default))]
169 pub mounts: Vec<FilesystemMount>,
170}
171
172impl Default for FilesystemCapabilities {
173 fn default() -> Self {
174 Self {
175 mode: FilesystemMode::None,
176 mounts: Vec::new(),
177 }
178 }
179}
180
181#[derive(Clone, Debug, PartialEq, Eq, Default)]
183#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
184#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
185#[cfg_attr(feature = "schemars", derive(JsonSchema))]
186pub enum FilesystemMode {
187 #[default]
189 None,
190 ReadOnly,
192 Sandbox,
194}
195
196#[derive(Clone, Debug, PartialEq, Eq)]
198#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
199#[cfg_attr(feature = "schemars", derive(JsonSchema))]
200pub struct FilesystemMount {
201 pub name: String,
203 pub host_class: String,
205 pub guest_path: String,
207}
208
209#[derive(Clone, Debug, PartialEq, Eq, Default)]
211#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
212#[cfg_attr(feature = "schemars", derive(JsonSchema))]
213pub struct EnvCapabilities {
214 #[cfg_attr(feature = "serde", serde(default))]
216 pub allow: Vec<String>,
217}
218
219#[derive(Clone, Debug, PartialEq, Eq, Default)]
221#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
222#[cfg_attr(feature = "schemars", derive(JsonSchema))]
223pub struct HostCapabilities {
224 #[cfg_attr(
226 feature = "serde",
227 serde(default, skip_serializing_if = "Option::is_none")
228 )]
229 pub secrets: Option<SecretsCapabilities>,
230 #[cfg_attr(
232 feature = "serde",
233 serde(default, skip_serializing_if = "Option::is_none")
234 )]
235 pub state: Option<StateCapabilities>,
236 #[cfg_attr(
238 feature = "serde",
239 serde(default, skip_serializing_if = "Option::is_none")
240 )]
241 pub messaging: Option<MessagingCapabilities>,
242 #[cfg_attr(
244 feature = "serde",
245 serde(default, skip_serializing_if = "Option::is_none")
246 )]
247 pub events: Option<EventsCapabilities>,
248 #[cfg_attr(
250 feature = "serde",
251 serde(default, skip_serializing_if = "Option::is_none")
252 )]
253 pub http: Option<HttpCapabilities>,
254 #[cfg_attr(
256 feature = "serde",
257 serde(default, skip_serializing_if = "Option::is_none")
258 )]
259 pub telemetry: Option<TelemetryCapabilities>,
260 #[cfg_attr(
262 feature = "serde",
263 serde(default, skip_serializing_if = "Option::is_none")
264 )]
265 pub iac: Option<IaCCapabilities>,
266}
267
268#[derive(Clone, Debug, PartialEq, Eq, Default)]
270#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
271#[cfg_attr(feature = "schemars", derive(JsonSchema))]
272pub struct SecretsCapabilities {
273 #[cfg_attr(feature = "serde", serde(default))]
275 pub required: Vec<String>,
276}
277
278#[derive(Clone, Debug, PartialEq, Eq, Default)]
280#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
281#[cfg_attr(feature = "schemars", derive(JsonSchema))]
282pub struct StateCapabilities {
283 #[cfg_attr(feature = "serde", serde(default))]
285 pub read: bool,
286 #[cfg_attr(feature = "serde", serde(default))]
288 pub write: bool,
289}
290
291#[derive(Clone, Debug, PartialEq, Eq, Default)]
293#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
294#[cfg_attr(feature = "schemars", derive(JsonSchema))]
295pub struct MessagingCapabilities {
296 #[cfg_attr(feature = "serde", serde(default))]
298 pub inbound: bool,
299 #[cfg_attr(feature = "serde", serde(default))]
301 pub outbound: bool,
302}
303
304#[derive(Clone, Debug, PartialEq, Eq, Default)]
306#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
307#[cfg_attr(feature = "schemars", derive(JsonSchema))]
308pub struct EventsCapabilities {
309 #[cfg_attr(feature = "serde", serde(default))]
311 pub inbound: bool,
312 #[cfg_attr(feature = "serde", serde(default))]
314 pub outbound: bool,
315}
316
317#[derive(Clone, Debug, PartialEq, Eq, Default)]
319#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
320#[cfg_attr(feature = "schemars", derive(JsonSchema))]
321pub struct HttpCapabilities {
322 #[cfg_attr(feature = "serde", serde(default))]
324 pub client: bool,
325 #[cfg_attr(feature = "serde", serde(default))]
327 pub server: bool,
328}
329
330#[derive(Clone, Debug, PartialEq, Eq, Hash)]
332#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
333#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
334#[cfg_attr(feature = "schemars", derive(JsonSchema))]
335pub enum TelemetryScope {
336 Tenant,
338 Pack,
340 Node,
342}
343
344#[derive(Clone, Debug, PartialEq, Eq)]
346#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
347#[cfg_attr(feature = "schemars", derive(JsonSchema))]
348pub struct TelemetryCapabilities {
349 pub scope: TelemetryScope,
351}
352
353#[derive(Clone, Debug, PartialEq, Eq)]
355#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
356#[cfg_attr(feature = "schemars", derive(JsonSchema))]
357pub struct IaCCapabilities {
358 pub write_templates: bool,
360 #[cfg_attr(feature = "serde", serde(default))]
362 pub execute_plans: bool,
363}
364
365#[derive(Clone, Debug, PartialEq, Eq)]
367pub enum ComponentProfileError {
368 UnsupportedProfile {
370 requested: String,
372 supported: Vec<String>,
374 },
375}
376
377impl core::fmt::Display for ComponentProfileError {
378 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
379 match self {
380 ComponentProfileError::UnsupportedProfile {
381 requested,
382 supported,
383 } => {
384 write!(
385 f,
386 "profile `{requested}` is not supported; known profiles: {supported:?}"
387 )
388 }
389 }
390 }
391}
392
393#[cfg(feature = "std")]
394impl std::error::Error for ComponentProfileError {}