Skip to main content

lenso_plugin_bundle/
selection.rs

1use lenso_app_plan::{ExecutionClassId, authoring::PluginDescriptor};
2
3use crate::{
4    BundleError, PluginArtifactV2, PluginImplementationV3, PluginManifest, invalid_bundle,
5};
6
7/// Host policy used to resolve one implementation before Plan construction.
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct ImplementationPolicy {
10    pub host_target: String,
11    pub execution_classes: Vec<ExecutionClassId>,
12}
13
14/// One final implementation selected from a Plugin Release.
15#[derive(Clone, Debug, Eq, PartialEq)]
16pub struct ResolvedPluginImplementation {
17    pub implementation_id: String,
18    pub descriptor: PluginDescriptor,
19    pub artifact: PluginArtifactV2,
20}
21
22/// Selects one implementation deterministically. Selection never implies runtime fallback.
23pub fn resolve_implementation(
24    manifest: &PluginManifest,
25    policy: &ImplementationPolicy,
26) -> Result<ResolvedPluginImplementation, BundleError> {
27    match manifest {
28        PluginManifest::V2(value) => {
29            let descriptor =
30                serde_json::from_value::<PluginDescriptor>(value.entry.descriptor.clone())
31                    .map_err(|error| BundleError::InvalidManifest(error.to_string()))?;
32            if !policy
33                .execution_classes
34                .contains(descriptor.execution_class())
35                || !target_matches(&value.artifact, &policy.host_target)
36            {
37                return invalid_bundle("V2 Bundle has no implementation admitted by Host policy");
38            }
39            Ok(ResolvedPluginImplementation {
40                implementation_id: "default".to_owned(),
41                descriptor,
42                artifact: value.artifact.clone(),
43            })
44        }
45        PluginManifest::V3(value) => {
46            for execution_class in &policy.execution_classes {
47                let matches = value
48                    .implementations
49                    .iter()
50                    .filter(|candidate| {
51                        candidate.runtime.execution_class() == execution_class
52                            && candidate
53                                .host_targets
54                                .iter()
55                                .any(|target| target == "*" || target == &policy.host_target)
56                    })
57                    .collect::<Vec<_>>();
58                match matches.as_slice() {
59                    [] => {}
60                    [candidate] => return Ok(resolved_v3(value, candidate)),
61                    _ => {
62                        return invalid_bundle(format!(
63                            "Host policy ambiguously matches {} implementations of `{}`",
64                            matches.len(),
65                            execution_class.as_str()
66                        ));
67                    }
68                }
69            }
70            invalid_bundle("V3 Bundle has no implementation admitted by Host policy")
71        }
72    }
73}
74
75fn resolved_v3(
76    manifest: &crate::PluginManifestV3,
77    candidate: &PluginImplementationV3,
78) -> ResolvedPluginImplementation {
79    ResolvedPluginImplementation {
80        implementation_id: candidate.id.clone(),
81        descriptor: manifest.contract.resolve(&candidate.runtime),
82        artifact: candidate.artifact.clone(),
83    }
84}
85
86fn target_matches(artifact: &PluginArtifactV2, host_target: &str) -> bool {
87    artifact.media_type == "application/wasm" || artifact.target == host_target
88}