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