use super::{
PluginComponentPath, PluginConfig, PluginIdentity, PluginProposalPolicy, PluginWitWorld,
};
use crate::plugin::{PluginCapabilities, PluginRegistryError, ValidatedPluginRuntimeLimits};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct PluginRegistryConfig {
pub plugins: Vec<PluginConfig>,
}
impl PluginRegistryConfig {
pub fn enabled_plugins(&self) -> impl Iterator<Item = &PluginConfig> {
self.plugins.iter().filter(|plugin| plugin.enabled)
}
#[must_use]
pub fn plugin(&self, identity: &str) -> Option<&PluginConfig> {
self.plugins
.iter()
.find(|plugin| plugin.identity == identity)
}
pub fn validate(&self) -> Result<ValidatedPluginRegistry, PluginRegistryError> {
ValidatedPluginRegistry::try_from(self.clone())
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct ValidatedPluginRegistry {
plugins: Vec<ValidatedPluginConfig>,
}
impl ValidatedPluginRegistry {
pub fn enabled_plugins(&self) -> impl Iterator<Item = EnabledPluginConfig<'_>> {
self.plugins
.iter()
.filter_map(ValidatedPluginConfig::enabled)
}
#[must_use]
pub fn enabled_plugin(&self, identity: &PluginIdentity) -> Option<EnabledPluginConfig<'_>> {
self.plugins
.iter()
.find(|plugin| plugin.identity() == identity)
.and_then(ValidatedPluginConfig::enabled)
}
#[must_use]
pub fn plugin(&self, identity: &PluginIdentity) -> Option<&PluginConfig> {
self.plugins
.iter()
.find(|plugin| plugin.identity() == identity)
.map(ValidatedPluginConfig::config)
}
#[must_use]
pub fn plugins(&self) -> impl ExactSizeIterator<Item = &ValidatedPluginConfig> {
self.plugins.iter()
}
}
impl TryFrom<PluginRegistryConfig> for ValidatedPluginRegistry {
type Error = PluginRegistryError;
fn try_from(config: PluginRegistryConfig) -> Result<Self, Self::Error> {
let mut identities = HashSet::with_capacity(config.plugins.len());
let mut identity_proofs = Vec::with_capacity(config.plugins.len());
for plugin in &config.plugins {
let identity = PluginIdentity::try_new(plugin.identity.clone()).map_err(|source| {
PluginRegistryError::InvalidIdentity {
identity: plugin.identity.clone(),
source,
}
})?;
if !identities.insert(identity.clone()) {
return Err(PluginRegistryError::DuplicateIdentity { identity });
}
identity_proofs.push(identity);
}
let mut plugins = Vec::with_capacity(config.plugins.len());
for (plugin, identity) in config.plugins.into_iter().zip(identity_proofs) {
let validated = ValidatedPluginConfig::from_validated_identity(identity, plugin)?;
plugins.push(validated);
}
Ok(Self { plugins })
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ValidatedPluginConfig {
identity: PluginIdentity,
config: PluginConfig,
enabled: Option<EnabledPluginConfigProof>,
}
impl ValidatedPluginConfig {
#[must_use]
pub const fn identity(&self) -> &PluginIdentity {
&self.identity
}
#[must_use]
pub const fn config(&self) -> &PluginConfig {
&self.config
}
#[must_use]
pub fn enabled(&self) -> Option<EnabledPluginConfig<'_>> {
self.enabled
.as_ref()
.map(|proof| EnabledPluginConfig { entry: self, proof })
}
}
impl TryFrom<PluginConfig> for ValidatedPluginConfig {
type Error = PluginRegistryError;
fn try_from(config: PluginConfig) -> Result<Self, Self::Error> {
let identity = PluginIdentity::try_new(config.identity.clone()).map_err(|source| {
PluginRegistryError::InvalidIdentity {
identity: config.identity.clone(),
source,
}
})?;
Self::from_validated_identity(identity, config)
}
}
impl ValidatedPluginConfig {
fn from_validated_identity(
identity: PluginIdentity,
config: PluginConfig,
) -> Result<Self, PluginRegistryError> {
let enabled = if config.enabled {
Some(EnabledPluginConfigProof::try_from_config(
&identity, &config,
)?)
} else {
None
};
Ok(Self {
identity,
config,
enabled,
})
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct EnabledPluginConfigProof {
wit_world: PluginWitWorld,
component_path: PluginComponentPath,
runtime_limits: ValidatedPluginRuntimeLimits,
}
impl EnabledPluginConfigProof {
fn try_from_config(
identity: &PluginIdentity,
config: &PluginConfig,
) -> Result<Self, PluginRegistryError> {
if !config.supports_current_world() {
return Err(PluginRegistryError::UnsupportedEnabledWitWorld {
identity: identity.clone(),
wit_world: config.wit_world.clone(),
});
}
let wit_world = PluginWitWorld::current();
let component_path =
PluginComponentPath::try_new(config.component_path.clone()).map_err(|source| {
PluginRegistryError::InvalidEnabledComponentPath {
identity: identity.clone(),
source,
}
})?;
let runtime_limits = config.runtime_limits.validate().map_err(|source| {
PluginRegistryError::InvalidEnabledRuntimeLimits {
identity: identity.clone(),
source,
}
})?;
Ok(Self {
wit_world,
component_path,
runtime_limits,
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EnabledPluginConfig<'registry> {
entry: &'registry ValidatedPluginConfig,
proof: &'registry EnabledPluginConfigProof,
}
impl EnabledPluginConfig<'_> {
#[must_use]
pub const fn identity(&self) -> &PluginIdentity {
&self.entry.identity
}
#[must_use]
pub const fn wit_world(&self) -> &'static str {
self.proof.wit_world.as_str()
}
#[must_use]
pub(in crate::plugin) const fn wit_world_proof(&self) -> PluginWitWorld {
self.proof.wit_world
}
#[must_use]
pub const fn component_path(&self) -> &PluginComponentPath {
&self.proof.component_path
}
#[must_use]
pub const fn capabilities(&self) -> &PluginCapabilities {
&self.entry.config.capabilities
}
#[must_use]
pub const fn runtime_limits(&self) -> ValidatedPluginRuntimeLimits {
self.proof.runtime_limits
}
#[must_use]
pub const fn proposal_policy(&self) -> PluginProposalPolicy {
self.entry.config.proposal_policy
}
#[must_use]
pub const fn config(&self) -> &PluginConfig {
&self.entry.config
}
}