Skip to main content

lenso_app_plan/authoring/plugin_root/
release.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use super::{PluginDescriptor, empty_configuration};
5use crate::{
6    CapabilityEndpointPlan, CapabilityRequirementPlan, ExecutionClassId, PluginCriticality,
7    RestartPolicy,
8};
9
10/// Runtime-independent contract shared by every implementation of one Plugin Release.
11#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
12#[serde(deny_unknown_fields)]
13pub struct PluginContract {
14    plugin_id: String,
15    release_version: String,
16    root_slot: String,
17    #[serde(default, skip_serializing_if = "Option::is_none")]
18    configuration_schema: Option<Value>,
19    #[serde(
20        default = "empty_configuration",
21        skip_serializing_if = "super::is_empty_configuration"
22    )]
23    configuration_defaults: Value,
24    provided_capabilities: Vec<CapabilityEndpointPlan>,
25    required_capabilities: Vec<CapabilityRequirementPlan>,
26    restart_policy: RestartPolicy,
27    criticality: PluginCriticality,
28}
29
30impl PluginContract {
31    pub fn new(
32        plugin_id: impl Into<String>,
33        release_version: impl Into<String>,
34        root_slot: impl Into<String>,
35    ) -> Self {
36        Self {
37            plugin_id: plugin_id.into(),
38            release_version: release_version.into(),
39            root_slot: root_slot.into(),
40            configuration_schema: None,
41            configuration_defaults: empty_configuration(),
42            provided_capabilities: Vec::new(),
43            required_capabilities: Vec::new(),
44            restart_policy: RestartPolicy::default(),
45            criticality: PluginCriticality::default(),
46        }
47    }
48
49    #[must_use]
50    pub fn with_configuration_schema(mut self, schema: Value) -> Self {
51        self.configuration_schema = Some(schema);
52        self
53    }
54
55    #[must_use]
56    pub fn with_configuration_defaults(mut self, defaults: Value) -> Self {
57        self.configuration_defaults = defaults;
58        self
59    }
60
61    #[must_use]
62    pub fn with_capability(mut self, capability: CapabilityEndpointPlan) -> Self {
63        self.provided_capabilities.push(capability);
64        self
65    }
66
67    #[must_use]
68    pub fn with_requirement(mut self, requirement: CapabilityRequirementPlan) -> Self {
69        self.required_capabilities.push(requirement);
70        self
71    }
72
73    #[must_use]
74    pub const fn with_restart_policy(mut self, restart_policy: RestartPolicy) -> Self {
75        self.restart_policy = restart_policy;
76        self
77    }
78
79    #[must_use]
80    pub const fn with_criticality(mut self, criticality: PluginCriticality) -> Self {
81        self.criticality = criticality;
82        self
83    }
84
85    pub fn plugin_id(&self) -> &str {
86        &self.plugin_id
87    }
88
89    pub fn release_version(&self) -> &str {
90        &self.release_version
91    }
92
93    pub fn root_slot(&self) -> &str {
94        &self.root_slot
95    }
96
97    pub const fn configuration_schema(&self) -> Option<&Value> {
98        self.configuration_schema.as_ref()
99    }
100
101    pub const fn configuration_defaults(&self) -> &Value {
102        &self.configuration_defaults
103    }
104
105    pub fn provided_capabilities(&self) -> &[CapabilityEndpointPlan] {
106        &self.provided_capabilities
107    }
108
109    pub fn required_capabilities(&self) -> &[CapabilityRequirementPlan] {
110        &self.required_capabilities
111    }
112
113    pub const fn restart_policy(&self) -> RestartPolicy {
114        self.restart_policy
115    }
116
117    pub const fn criticality(&self) -> PluginCriticality {
118        self.criticality
119    }
120
121    /// Resolves this stable contract against one exact executable implementation.
122    pub fn resolve(&self, implementation: &PluginImplementation) -> PluginDescriptor {
123        let mut descriptor =
124            PluginDescriptor::new(&self.plugin_id, &self.release_version, &self.root_slot)
125                .with_runtime_package(
126                    &implementation.runtime_package_id,
127                    &implementation.runtime_package_revision,
128                )
129                .with_entrypoint(&implementation.entrypoint)
130                .with_configuration_defaults(self.configuration_defaults.clone())
131                .with_execution_class(implementation.execution_class.clone())
132                .with_restart_policy(self.restart_policy)
133                .with_criticality(self.criticality);
134        if let Some(schema) = &self.configuration_schema {
135            descriptor = descriptor.with_configuration_schema(schema.clone());
136        }
137        for capability in &self.provided_capabilities {
138            descriptor = descriptor.with_capability(capability.clone());
139        }
140        for requirement in &self.required_capabilities {
141            descriptor = descriptor.with_requirement(requirement.clone());
142        }
143        descriptor
144    }
145}
146
147/// Exact runtime selection used to execute one Plugin Contract.
148#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
149#[serde(deny_unknown_fields)]
150pub struct PluginImplementation {
151    runtime_package_id: String,
152    runtime_package_revision: String,
153    entrypoint: String,
154    execution_class: ExecutionClassId,
155}
156
157impl PluginImplementation {
158    pub fn new(
159        runtime_package_id: impl Into<String>,
160        runtime_package_revision: impl Into<String>,
161        entrypoint: impl Into<String>,
162        execution_class: ExecutionClassId,
163    ) -> Self {
164        Self {
165            runtime_package_id: runtime_package_id.into(),
166            runtime_package_revision: runtime_package_revision.into(),
167            entrypoint: entrypoint.into(),
168            execution_class,
169        }
170    }
171
172    pub fn runtime_package_id(&self) -> &str {
173        &self.runtime_package_id
174    }
175
176    pub fn runtime_package_revision(&self) -> &str {
177        &self.runtime_package_revision
178    }
179
180    pub fn entrypoint(&self) -> &str {
181        &self.entrypoint
182    }
183
184    pub const fn execution_class(&self) -> &ExecutionClassId {
185        &self.execution_class
186    }
187}
188
189impl PluginDescriptor {
190    /// Projects the runtime-independent Contract from this resolved descriptor.
191    pub fn contract(&self) -> PluginContract {
192        PluginContract {
193            plugin_id: self.plugin_id.clone(),
194            release_version: self.release_version.clone(),
195            root_slot: self.root_slot.clone(),
196            configuration_schema: self.configuration_schema.clone(),
197            configuration_defaults: self.configuration_defaults.clone(),
198            provided_capabilities: self.provided_capabilities.clone(),
199            required_capabilities: self.required_capabilities.clone(),
200            restart_policy: self.restart_policy,
201            criticality: self.criticality,
202        }
203    }
204
205    /// Projects the exact runtime selection from this resolved descriptor.
206    pub fn implementation(&self) -> PluginImplementation {
207        PluginImplementation {
208            runtime_package_id: self.runtime_package_id.clone(),
209            runtime_package_revision: self.runtime_package_revision.clone(),
210            entrypoint: self.entrypoint.clone(),
211            execution_class: self.execution_class.clone(),
212        }
213    }
214}