use super::*;
use crate::registry_core::declaration::{FlowContract, FrameworkId, PluginMode, PluginSource};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PluginChannel {
Official,
Community,
Local,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SlotValidationError {
Policy(String),
Contract(String),
}
pub fn validate_operation_name(name: &str) -> Result<(), String> {
if name.is_empty()
|| !name
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || byte == b'_')
{
return Err(format!("invalid plugin operation `{name}`"));
}
Ok(())
}
pub fn validate_artifact(
slot_name: &str,
slot_framework: FrameworkId,
slot_mode: PluginMode,
slot_contract: FlowContract,
slot_channels: &[PluginChannel],
channel: PluginChannel,
artifact: &VerifiedPluginArtifact,
) -> Result<(), SlotValidationError> {
if !slot_channels.contains(&channel) {
return Err(SlotValidationError::Policy(format!(
"plugin slot `{}` does not allow the {channel:?} channel",
slot_name
)));
}
let registration = artifact.registration();
let manifest = registration
.plugin
.ok_or_else(|| SlotValidationError::Policy("verified plugin has no manifest".to_owned()))?;
if manifest.framework != slot_framework {
return Err(SlotValidationError::Policy(format!(
"plugin targets `{}`, slot `{}` belongs to `{}`",
manifest.framework, slot_name, slot_framework
)));
}
if manifest.mode != slot_mode {
return Err(SlotValidationError::Policy(format!(
"plugin mode {:?} does not match slot mode {:?}",
manifest.mode, slot_mode
)));
}
match channel {
PluginChannel::Official
if manifest.source != PluginSource::Official
|| artifact.assurance() != PluginAssurance::Signature =>
{
return Err(SlotValidationError::Policy(
"official plugins require an official manifest and verified signature".to_owned(),
));
}
PluginChannel::Community | PluginChannel::Local
if manifest.source != PluginSource::User =>
{
return Err(SlotValidationError::Policy(
"community and local channels accept user manifests only".to_owned(),
));
}
_ => {}
}
if slot_mode == PluginMode::Replacement && !registration.flow.is_declared() {
return Err(SlotValidationError::Contract(
"replacement plugin has no flow contract".to_owned(),
));
}
if slot_contract.is_declared()
&& !registration
.flow
.semantically_compatible_with(slot_contract)
{
return Err(SlotValidationError::Contract(format!(
"plugin flow {:?} does not match slot flow {:?}",
registration.flow, slot_contract
)));
}
Ok(())
}