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)]
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 #[cfg_attr(feature = "serde", serde(default))]
46 pub operations: Vec<ComponentOperation>,
47 #[cfg_attr(
49 feature = "serde",
50 serde(default, skip_serializing_if = "Option::is_none")
51 )]
52 pub config_schema: Option<serde_json::Value>,
53 #[cfg_attr(feature = "serde", serde(default))]
55 pub resources: ResourceHints,
56}
57
58impl ComponentManifest {
59 pub fn supports_kind(&self, kind: FlowKind) -> bool {
61 self.supports.iter().copied().any(|entry| entry == kind)
62 }
63
64 pub fn select_profile<'a>(
67 &'a self,
68 requested: Option<&str>,
69 ) -> Result<Option<&'a str>, ComponentProfileError> {
70 if let Some(name) = requested {
71 let matched = self
72 .profiles
73 .supported
74 .iter()
75 .find(|candidate| candidate.as_str() == name)
76 .ok_or_else(|| ComponentProfileError::UnsupportedProfile {
77 requested: name.to_owned(),
78 supported: self.profiles.supported.clone(),
79 })?;
80 Ok(Some(matched.as_str()))
81 } else {
82 Ok(self.profiles.default.as_deref())
83 }
84 }
85
86 pub fn basic_configurator(&self) -> Option<&FlowId> {
88 self.configurators
89 .as_ref()
90 .and_then(|cfg| cfg.basic.as_ref())
91 }
92
93 pub fn full_configurator(&self) -> Option<&FlowId> {
95 self.configurators
96 .as_ref()
97 .and_then(|cfg| cfg.full.as_ref())
98 }
99}
100
101#[derive(Clone, Debug, PartialEq, Eq, Default)]
103#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
104#[cfg_attr(feature = "schemars", derive(JsonSchema))]
105pub struct ComponentProfiles {
106 #[cfg_attr(
108 feature = "serde",
109 serde(default, skip_serializing_if = "Option::is_none")
110 )]
111 pub default: Option<String>,
112 #[cfg_attr(feature = "serde", serde(default))]
114 pub supported: Vec<String>,
115}
116
117#[derive(Clone, Debug, PartialEq, Eq)]
119#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
120#[cfg_attr(feature = "schemars", derive(JsonSchema))]
121pub struct ComponentConfigurators {
122 #[cfg_attr(
124 feature = "serde",
125 serde(default, skip_serializing_if = "Option::is_none")
126 )]
127 pub basic: Option<FlowId>,
128 #[cfg_attr(
130 feature = "serde",
131 serde(default, skip_serializing_if = "Option::is_none")
132 )]
133 pub full: Option<FlowId>,
134}
135
136#[derive(Clone, Debug, PartialEq)]
138#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
139#[cfg_attr(feature = "schemars", derive(JsonSchema))]
140pub struct ComponentOperation {
141 pub name: String,
143 pub input_schema: serde_json::Value,
145 pub output_schema: serde_json::Value,
147}
148
149#[derive(Clone, Debug, PartialEq, Eq, Default)]
151#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
152#[cfg_attr(feature = "schemars", derive(JsonSchema))]
153pub struct ResourceHints {
154 #[cfg_attr(
156 feature = "serde",
157 serde(default, skip_serializing_if = "Option::is_none")
158 )]
159 pub cpu_millis: Option<u32>,
160 #[cfg_attr(
162 feature = "serde",
163 serde(default, skip_serializing_if = "Option::is_none")
164 )]
165 pub memory_mb: Option<u32>,
166 #[cfg_attr(
168 feature = "serde",
169 serde(default, skip_serializing_if = "Option::is_none")
170 )]
171 pub average_latency_ms: Option<u32>,
172}
173
174#[derive(Clone, Debug, PartialEq, Eq, Default)]
176#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
177#[cfg_attr(feature = "schemars", derive(JsonSchema))]
178pub struct ComponentCapabilities {
179 pub wasi: WasiCapabilities,
181 pub host: HostCapabilities,
183}
184
185#[derive(Clone, Debug, PartialEq, Eq, Default)]
187#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
188#[cfg_attr(feature = "schemars", derive(JsonSchema))]
189pub struct WasiCapabilities {
190 #[cfg_attr(
192 feature = "serde",
193 serde(default, skip_serializing_if = "Option::is_none")
194 )]
195 pub filesystem: Option<FilesystemCapabilities>,
196 #[cfg_attr(
198 feature = "serde",
199 serde(default, skip_serializing_if = "Option::is_none")
200 )]
201 pub env: Option<EnvCapabilities>,
202 #[cfg_attr(feature = "serde", serde(default))]
204 pub random: bool,
205 #[cfg_attr(feature = "serde", serde(default))]
207 pub clocks: bool,
208}
209
210#[derive(Clone, Debug, PartialEq, Eq)]
212#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
213#[cfg_attr(feature = "schemars", derive(JsonSchema))]
214pub struct FilesystemCapabilities {
215 pub mode: FilesystemMode,
217 #[cfg_attr(feature = "serde", serde(default))]
219 pub mounts: Vec<FilesystemMount>,
220}
221
222impl Default for FilesystemCapabilities {
223 fn default() -> Self {
224 Self {
225 mode: FilesystemMode::None,
226 mounts: Vec::new(),
227 }
228 }
229}
230
231#[derive(Clone, Debug, PartialEq, Eq, Default)]
233#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
234#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
235#[cfg_attr(feature = "schemars", derive(JsonSchema))]
236pub enum FilesystemMode {
237 #[default]
239 None,
240 ReadOnly,
242 Sandbox,
244}
245
246#[derive(Clone, Debug, PartialEq, Eq)]
248#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
249#[cfg_attr(feature = "schemars", derive(JsonSchema))]
250pub struct FilesystemMount {
251 pub name: String,
253 pub host_class: String,
255 pub guest_path: String,
257}
258
259#[derive(Clone, Debug, PartialEq, Eq, Default)]
261#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
262#[cfg_attr(feature = "schemars", derive(JsonSchema))]
263pub struct EnvCapabilities {
264 #[cfg_attr(feature = "serde", serde(default))]
266 pub allow: Vec<String>,
267}
268
269#[derive(Clone, Debug, PartialEq, Eq, Default)]
271#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
272#[cfg_attr(feature = "schemars", derive(JsonSchema))]
273pub struct HostCapabilities {
274 #[cfg_attr(
276 feature = "serde",
277 serde(default, skip_serializing_if = "Option::is_none")
278 )]
279 pub secrets: Option<SecretsCapabilities>,
280 #[cfg_attr(
282 feature = "serde",
283 serde(default, skip_serializing_if = "Option::is_none")
284 )]
285 pub state: Option<StateCapabilities>,
286 #[cfg_attr(
288 feature = "serde",
289 serde(default, skip_serializing_if = "Option::is_none")
290 )]
291 pub messaging: Option<MessagingCapabilities>,
292 #[cfg_attr(
294 feature = "serde",
295 serde(default, skip_serializing_if = "Option::is_none")
296 )]
297 pub events: Option<EventsCapabilities>,
298 #[cfg_attr(
300 feature = "serde",
301 serde(default, skip_serializing_if = "Option::is_none")
302 )]
303 pub http: Option<HttpCapabilities>,
304 #[cfg_attr(
306 feature = "serde",
307 serde(default, skip_serializing_if = "Option::is_none")
308 )]
309 pub telemetry: Option<TelemetryCapabilities>,
310 #[cfg_attr(
312 feature = "serde",
313 serde(default, skip_serializing_if = "Option::is_none")
314 )]
315 pub iac: Option<IaCCapabilities>,
316}
317
318#[derive(Clone, Debug, PartialEq, Eq, Default)]
320#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
321#[cfg_attr(feature = "schemars", derive(JsonSchema))]
322pub struct SecretsCapabilities {
323 #[cfg_attr(feature = "serde", serde(default))]
325 pub required: Vec<String>,
326}
327
328#[derive(Clone, Debug, PartialEq, Eq, Default)]
330#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
331#[cfg_attr(feature = "schemars", derive(JsonSchema))]
332pub struct StateCapabilities {
333 #[cfg_attr(feature = "serde", serde(default))]
335 pub read: bool,
336 #[cfg_attr(feature = "serde", serde(default))]
338 pub write: bool,
339}
340
341#[derive(Clone, Debug, PartialEq, Eq, Default)]
343#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
344#[cfg_attr(feature = "schemars", derive(JsonSchema))]
345pub struct MessagingCapabilities {
346 #[cfg_attr(feature = "serde", serde(default))]
348 pub inbound: bool,
349 #[cfg_attr(feature = "serde", serde(default))]
351 pub outbound: bool,
352}
353
354#[derive(Clone, Debug, PartialEq, Eq, Default)]
356#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
357#[cfg_attr(feature = "schemars", derive(JsonSchema))]
358pub struct EventsCapabilities {
359 #[cfg_attr(feature = "serde", serde(default))]
361 pub inbound: bool,
362 #[cfg_attr(feature = "serde", serde(default))]
364 pub outbound: bool,
365}
366
367#[derive(Clone, Debug, PartialEq, Eq, Default)]
369#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
370#[cfg_attr(feature = "schemars", derive(JsonSchema))]
371pub struct HttpCapabilities {
372 #[cfg_attr(feature = "serde", serde(default))]
374 pub client: bool,
375 #[cfg_attr(feature = "serde", serde(default))]
377 pub server: bool,
378}
379
380#[derive(Clone, Debug, PartialEq, Eq, Hash)]
382#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
383#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
384#[cfg_attr(feature = "schemars", derive(JsonSchema))]
385pub enum TelemetryScope {
386 Tenant,
388 Pack,
390 Node,
392}
393
394#[derive(Clone, Debug, PartialEq, Eq)]
396#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
397#[cfg_attr(feature = "schemars", derive(JsonSchema))]
398pub struct TelemetryCapabilities {
399 pub scope: TelemetryScope,
401}
402
403#[derive(Clone, Debug, PartialEq, Eq)]
405#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
406#[cfg_attr(feature = "schemars", derive(JsonSchema))]
407pub struct IaCCapabilities {
408 pub write_templates: bool,
410 #[cfg_attr(feature = "serde", serde(default))]
412 pub execute_plans: bool,
413}
414
415#[derive(Clone, Debug, PartialEq, Eq)]
417pub enum ComponentProfileError {
418 UnsupportedProfile {
420 requested: String,
422 supported: Vec<String>,
424 },
425}
426
427impl core::fmt::Display for ComponentProfileError {
428 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
429 match self {
430 ComponentProfileError::UnsupportedProfile {
431 requested,
432 supported,
433 } => {
434 write!(
435 f,
436 "profile `{requested}` is not supported; known profiles: {supported:?}"
437 )
438 }
439 }
440 }
441}
442
443#[cfg(feature = "std")]
444impl std::error::Error for ComponentProfileError {}