use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use semver::Version;
use crate::flow::FlowKind;
use crate::{ComponentId, FlowId, SecretRequirement};
#[cfg(feature = "schemars")]
use schemars::JsonSchema;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ComponentDevFlow {
#[cfg_attr(feature = "serde", serde(default = "dev_flow_default_format"))]
pub format: String,
pub graph: serde_json::Value,
}
fn dev_flow_default_format() -> String {
"flow-ir-json".to_owned()
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ComponentManifest {
pub id: ComponentId,
#[cfg_attr(
feature = "schemars",
schemars(with = "String", description = "SemVer version")
)]
pub version: Version,
#[cfg_attr(feature = "serde", serde(default))]
pub supports: Vec<FlowKind>,
pub world: String,
pub profiles: ComponentProfiles,
pub capabilities: ComponentCapabilities,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub configurators: Option<ComponentConfigurators>,
#[cfg_attr(feature = "serde", serde(default))]
pub operations: Vec<ComponentOperation>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub config_schema: Option<serde_json::Value>,
#[cfg_attr(feature = "serde", serde(default))]
pub resources: ResourceHints,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "BTreeMap::is_empty")
)]
pub dev_flows: BTreeMap<FlowId, ComponentDevFlow>,
}
impl ComponentManifest {
pub fn supports_kind(&self, kind: FlowKind) -> bool {
self.supports.iter().copied().any(|entry| entry == kind)
}
pub fn select_profile<'a>(
&'a self,
requested: Option<&str>,
) -> Result<Option<&'a str>, ComponentProfileError> {
if let Some(name) = requested {
let matched = self
.profiles
.supported
.iter()
.find(|candidate| candidate.as_str() == name)
.ok_or_else(|| ComponentProfileError::UnsupportedProfile {
requested: name.to_owned(),
supported: self.profiles.supported.clone(),
})?;
Ok(Some(matched.as_str()))
} else {
Ok(self.profiles.default.as_deref())
}
}
pub fn basic_configurator(&self) -> Option<&FlowId> {
self.configurators
.as_ref()
.and_then(|cfg| cfg.basic.as_ref())
}
pub fn full_configurator(&self) -> Option<&FlowId> {
self.configurators
.as_ref()
.and_then(|cfg| cfg.full.as_ref())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ComponentProfiles {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub default: Option<String>,
#[cfg_attr(feature = "serde", serde(default))]
pub supported: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ComponentConfigurators {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub basic: Option<FlowId>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub full: Option<FlowId>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ComponentOperation {
pub name: String,
pub input_schema: serde_json::Value,
pub output_schema: serde_json::Value,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ResourceHints {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub cpu_millis: Option<u32>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub memory_mb: Option<u32>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub average_latency_ms: Option<u32>,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ComponentCapabilities {
pub wasi: WasiCapabilities,
pub host: HostCapabilities,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct WasiCapabilities {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub filesystem: Option<FilesystemCapabilities>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub env: Option<EnvCapabilities>,
#[cfg_attr(feature = "serde", serde(default))]
pub random: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub clocks: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct FilesystemCapabilities {
pub mode: FilesystemMode,
#[cfg_attr(feature = "serde", serde(default))]
pub mounts: Vec<FilesystemMount>,
}
impl Default for FilesystemCapabilities {
fn default() -> Self {
Self {
mode: FilesystemMode::None,
mounts: Vec::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum FilesystemMode {
#[default]
None,
ReadOnly,
Sandbox,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct FilesystemMount {
pub name: String,
pub host_class: String,
pub guest_path: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct EnvCapabilities {
#[cfg_attr(feature = "serde", serde(default))]
pub allow: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct HostCapabilities {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub secrets: Option<SecretsCapabilities>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub state: Option<StateCapabilities>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub messaging: Option<MessagingCapabilities>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub events: Option<EventsCapabilities>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub http: Option<HttpCapabilities>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub telemetry: Option<TelemetryCapabilities>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub iac: Option<IaCCapabilities>,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct SecretsCapabilities {
#[cfg_attr(feature = "serde", serde(default))]
pub required: Vec<SecretRequirement>,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct StateCapabilities {
#[cfg_attr(feature = "serde", serde(default))]
pub read: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub write: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct MessagingCapabilities {
#[cfg_attr(feature = "serde", serde(default))]
pub inbound: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub outbound: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct EventsCapabilities {
#[cfg_attr(feature = "serde", serde(default))]
pub inbound: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub outbound: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct HttpCapabilities {
#[cfg_attr(feature = "serde", serde(default))]
pub client: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub server: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum TelemetryScope {
Tenant,
Pack,
Node,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct TelemetryCapabilities {
pub scope: TelemetryScope,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct IaCCapabilities {
pub write_templates: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub execute_plans: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ComponentProfileError {
UnsupportedProfile {
requested: String,
supported: Vec<String>,
},
}
impl core::fmt::Display for ComponentProfileError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ComponentProfileError::UnsupportedProfile {
requested,
supported,
} => {
write!(
f,
"profile `{requested}` is not supported; known profiles: {supported:?}"
)
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ComponentProfileError {}