Skip to main content

lenso_app_plan/
contract.rs

1//! Consumer declarations and exact Plugin execution and binding metadata.
2
3use super::{
4    CapabilityCardinality, CapabilityEndpointPlan, EventAdmissionPlan, ExecutionClassId,
5    ExecutionLaneId, PluginCriticality, RequestAdmissionPlan, RestartPolicy, schema,
6};
7use serde::{Deserialize, Serialize};
8
9/// One Capability required by a Plugin Instance.
10#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
11#[serde(from = "schema::RequirementWire")]
12pub struct CapabilityRequirementPlan {
13    #[serde(default)]
14    pub(super) requirement_id: String,
15    pub(super) capability_id: String,
16    pub(super) descriptor_version: String,
17    pub(super) cardinality: CapabilityCardinality,
18}
19
20impl CapabilityRequirementPlan {
21    /// Declares one exact Capability Descriptor and its binding cardinality.
22    pub fn new(
23        capability_id: impl Into<String>,
24        descriptor_version: impl Into<String>,
25        cardinality: CapabilityCardinality,
26    ) -> Self {
27        let capability_id = capability_id.into();
28        Self {
29            requirement_id: format!("~{capability_id}"),
30            capability_id,
31            descriptor_version: descriptor_version.into(),
32            cardinality,
33        }
34    }
35
36    /// Declares a required one-provider Capability.
37    pub fn one(capability_id: impl Into<String>, descriptor_version: impl Into<String>) -> Self {
38        Self::new(
39            capability_id,
40            descriptor_version,
41            CapabilityCardinality::One,
42        )
43    }
44
45    /// Declares an optional zero-or-one-provider Capability.
46    pub fn optional(
47        capability_id: impl Into<String>,
48        descriptor_version: impl Into<String>,
49    ) -> Self {
50        Self::new(
51            capability_id,
52            descriptor_version,
53            CapabilityCardinality::Optional,
54        )
55    }
56
57    /// Declares a many-provider Capability.
58    pub fn many(capability_id: impl Into<String>, descriptor_version: impl Into<String>) -> Self {
59        Self::new(
60            capability_id,
61            descriptor_version,
62            CapabilityCardinality::Many,
63        )
64    }
65
66    /// Names this dependency within its consumer's version 2 contract.
67    #[must_use]
68    pub fn with_requirement_id(mut self, requirement_id: impl Into<String>) -> Self {
69        self.requirement_id = requirement_id.into();
70        self
71    }
72
73    /// Returns the consumer-local identity, including normalized old declarations.
74    pub fn requirement_id(&self) -> &str {
75        &self.requirement_id
76    }
77
78    /// Returns the Capability series identity.
79    pub fn capability_id(&self) -> &str {
80        &self.capability_id
81    }
82
83    /// Returns the exact Descriptor version selected by Composition.
84    pub fn descriptor_version(&self) -> &str {
85        &self.descriptor_version
86    }
87
88    /// Returns the requirement cardinality.
89    pub const fn cardinality(&self) -> CapabilityCardinality {
90        self.cardinality
91    }
92}
93
94/// One exact App-local Plugin Instance selected by the resolved Plan.
95#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
96pub struct PluginInstancePlan {
97    #[serde(default = "schema::old_authoring_version")]
98    pub(super) authoring_version: u32,
99    #[serde(default)]
100    pub(super) runtime_profile: String,
101    pub(super) instance_key: String,
102    pub(super) package_id: String,
103    pub(super) entrypoint: String,
104    pub(super) configuration: String,
105    pub(super) provided_capabilities: Vec<CapabilityEndpointPlan>,
106    pub(super) required_capabilities: Vec<CapabilityRequirementPlan>,
107    pub(super) execution_class: ExecutionClassId,
108    pub(super) package_revision: String,
109    pub(super) restart_policy: RestartPolicy,
110    pub(super) criticality: PluginCriticality,
111    #[serde(default)]
112    pub(super) execution_lane: ExecutionLaneId,
113}
114
115impl PluginInstancePlan {
116    /// Selects one statically linked package under an App-local Instance key.
117    pub fn new(instance_key: impl Into<String>, package_id: impl Into<String>) -> Self {
118        Self {
119            authoring_version: 1,
120            runtime_profile: "lenso.native-authoring@1".to_owned(),
121            instance_key: instance_key.into(),
122            package_id: package_id.into(),
123            entrypoint: "default".to_owned(),
124            configuration: "{}".to_owned(),
125            provided_capabilities: Vec::new(),
126            required_capabilities: Vec::new(),
127            execution_class: ExecutionClassId::native_rust(),
128            package_revision: String::new(),
129            restart_policy: RestartPolicy::default(),
130            criticality: PluginCriticality::default(),
131            execution_lane: ExecutionLaneId::default(),
132        }
133    }
134
135    /// Selects the exact package entrypoint executed for this Instance.
136    #[must_use]
137    pub fn with_authoring(mut self, version: u32, runtime_profile: impl Into<String>) -> Self {
138        self.authoring_version = version;
139        self.runtime_profile = runtime_profile.into();
140        self
141    }
142
143    /// Returns the selected Plugin contract version.
144    pub const fn authoring_version(&self) -> u32 {
145        self.authoring_version
146    }
147
148    /// Returns the exact opaque execution profile, independently of execution class.
149    pub fn runtime_profile(&self) -> &str {
150        &self.runtime_profile
151    }
152
153    /// Selects the exact package entrypoint executed for this Instance.
154    #[must_use]
155    pub fn with_entrypoint(mut self, entrypoint: impl Into<String>) -> Self {
156        self.entrypoint = entrypoint.into();
157        self
158    }
159
160    /// Supplies opaque, non-secret configuration owned and decoded by the Plugin.
161    #[must_use]
162    pub fn with_configuration(mut self, configuration: impl Into<String>) -> Self {
163        self.configuration = configuration.into();
164        self
165    }
166
167    /// Declares one exact endpoint this Instance must prepare.
168    #[must_use]
169    pub fn with_capability(mut self, capability: CapabilityEndpointPlan) -> Self {
170        self.provided_capabilities.push(capability);
171        self
172    }
173
174    /// Declares one Capability dependency for this Instance.
175    #[must_use]
176    pub fn with_requirement(mut self, requirement: CapabilityRequirementPlan) -> Self {
177        self.required_capabilities.push(requirement);
178        self
179    }
180
181    /// Alias that makes the authoring direction explicit at the call site.
182    #[must_use]
183    pub fn with_required_capability(self, requirement: CapabilityRequirementPlan) -> Self {
184        self.with_requirement(requirement)
185    }
186
187    /// Selects the host execution class for this Plugin Instance.
188    #[must_use]
189    pub fn with_execution_class(mut self, execution_class: ExecutionClassId) -> Self {
190        if self.authoring_version == 1
191            && self.runtime_profile == schema::old_runtime_profile(&self.execution_class)
192        {
193            self.runtime_profile = schema::old_runtime_profile(&execution_class);
194        }
195        self.execution_class = execution_class;
196        self
197    }
198
199    /// Places this Plugin Instance on one Plan-declared Execution Lane.
200    #[must_use]
201    pub fn with_execution_lane(mut self, execution_lane: ExecutionLaneId) -> Self {
202        self.execution_lane = execution_lane;
203        self
204    }
205
206    /// Records the exact opaque package-manager lock selection before boot.
207    #[must_use]
208    pub fn with_package_revision(mut self, revision: impl Into<String>) -> Self {
209        self.package_revision = revision.into();
210        self
211    }
212
213    /// Selects the finite supervision policy for this Plugin Instance.
214    #[must_use]
215    pub fn with_restart_policy(mut self, restart_policy: RestartPolicy) -> Self {
216        self.restart_policy = restart_policy;
217        self
218    }
219
220    /// Marks this Plugin Instance critical for supervision exhaustion outcomes.
221    #[must_use]
222    pub fn with_criticality(mut self, criticality: PluginCriticality) -> Self {
223        self.criticality = criticality;
224        self
225    }
226
227    /// Returns the App-local Instance key.
228    pub fn instance_key(&self) -> &str {
229        &self.instance_key
230    }
231
232    /// Returns the selected package identity.
233    pub fn package_id(&self) -> &str {
234        &self.package_id
235    }
236
237    /// Returns the exact package entrypoint selected before boot.
238    pub fn entrypoint(&self) -> &str {
239        &self.entrypoint
240    }
241
242    /// Returns the Plugin-owned opaque configuration selected before boot.
243    pub fn configuration(&self) -> &str {
244        &self.configuration
245    }
246
247    /// Returns the exact endpoint set this Instance must prepare.
248    pub fn provided_capabilities(&self) -> &[CapabilityEndpointPlan] {
249        &self.provided_capabilities
250    }
251
252    /// Returns the exact Capability requirements this Instance receives.
253    pub fn required_capabilities(&self) -> &[CapabilityRequirementPlan] {
254        &self.required_capabilities
255    }
256
257    /// Returns the host execution class selected for this Instance.
258    pub fn execution_class(&self) -> &ExecutionClassId {
259        &self.execution_class
260    }
261
262    /// Returns the Plan-declared Execution Lane for this Instance.
263    pub const fn execution_lane(&self) -> &ExecutionLaneId {
264        &self.execution_lane
265    }
266
267    /// Returns the exact opaque package-manager lock selection.
268    pub fn package_revision(&self) -> &str {
269        &self.package_revision
270    }
271
272    /// Returns the supervision policy selected for this Instance.
273    pub const fn restart_policy(&self) -> RestartPolicy {
274        self.restart_policy
275    }
276
277    /// Returns the criticality selected for this Instance.
278    pub const fn criticality(&self) -> PluginCriticality {
279        self.criticality
280    }
281}
282
283/// One exact consumer-to-provider Capability binding.
284#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
285#[serde(from = "schema::BindingWire")]
286pub struct CapabilityBinding {
287    #[serde(default)]
288    pub(super) requirement_id: String,
289    pub(super) consumer_instance: String,
290    pub(super) capability_id: String,
291    pub(super) descriptor_version: String,
292    pub(super) provider_instance: String,
293    pub(super) provider_order: usize,
294    pub(super) admission: RequestAdmissionPlan,
295    pub(super) admission_explicit: bool,
296    pub(super) event_admission: EventAdmissionPlan,
297    pub(super) event_admission_explicit: bool,
298}
299
300impl CapabilityBinding {
301    /// Binds one consumer to one provider at an exact Descriptor version.
302    pub fn new(
303        consumer_instance: impl Into<String>,
304        capability_id: impl Into<String>,
305        descriptor_version: impl Into<String>,
306        provider_instance: impl Into<String>,
307    ) -> Self {
308        let capability_id = capability_id.into();
309        Self {
310            requirement_id: format!("~{capability_id}"),
311            consumer_instance: consumer_instance.into(),
312            capability_id,
313            descriptor_version: descriptor_version.into(),
314            provider_instance: provider_instance.into(),
315            provider_order: 0,
316            admission: RequestAdmissionPlan::default(),
317            admission_explicit: false,
318            event_admission: EventAdmissionPlan::default(),
319            event_admission_explicit: false,
320        }
321    }
322
323    /// Selects the consumer-local named requirement.
324    #[must_use]
325    pub fn with_requirement_id(mut self, requirement_id: impl Into<String>) -> Self {
326        self.requirement_id = requirement_id.into();
327        self
328    }
329
330    /// Returns the consumer-local requirement selected by this binding.
331    pub fn requirement_id(&self) -> &str {
332        &self.requirement_id
333    }
334
335    /// Overrides the provider Operation admission policy for this binding.
336    #[must_use]
337    pub fn with_admission(mut self, admission: RequestAdmissionPlan) -> Self {
338        self.admission = admission;
339        self.admission_explicit = true;
340        self
341    }
342
343    /// Overrides queue and concurrency limits for this binding.
344    #[must_use]
345    pub fn with_limits(self, queue_capacity: usize, max_concurrency: usize) -> Self {
346        self.with_admission(RequestAdmissionPlan::new(queue_capacity, max_concurrency))
347    }
348
349    /// Overrides the Event mailbox policy for this binding.
350    #[must_use]
351    pub fn with_event_admission(mut self, admission: EventAdmissionPlan) -> Self {
352        self.event_admission = admission;
353        self.event_admission_explicit = true;
354        self
355    }
356
357    /// Overrides the Event mailbox capacity for this binding.
358    #[must_use]
359    pub fn with_event_capacity(self, capacity: usize) -> Self {
360        self.with_event_admission(EventAdmissionPlan::new(capacity))
361    }
362
363    pub(super) fn with_provider_order(mut self, provider_order: usize) -> Self {
364        self.provider_order = provider_order;
365        self
366    }
367
368    /// Returns the consumer Instance key.
369    pub fn consumer_instance(&self) -> &str {
370        &self.consumer_instance
371    }
372
373    /// Returns the Capability series identity.
374    pub fn capability_id(&self) -> &str {
375        &self.capability_id
376    }
377
378    /// Returns the exact Descriptor version.
379    pub fn descriptor_version(&self) -> &str {
380        &self.descriptor_version
381    }
382
383    /// Returns the provider Instance key.
384    pub fn provider_instance(&self) -> &str {
385        &self.provider_instance
386    }
387
388    /// Returns the deterministic zero-based order within a `many` requirement.
389    pub const fn provider_order(&self) -> usize {
390        self.provider_order
391    }
392
393    /// Returns the binding's effective fallback admission policy.
394    pub const fn admission(&self) -> RequestAdmissionPlan {
395        self.admission
396    }
397
398    /// Returns whether this binding explicitly overrides the provider policy.
399    pub const fn has_explicit_admission(&self) -> bool {
400        self.admission_explicit
401    }
402
403    /// Returns the binding's effective fallback Event mailbox policy.
404    pub const fn event_admission(&self) -> EventAdmissionPlan {
405        self.event_admission
406    }
407
408    /// Returns whether this binding explicitly overrides the provider Event policy.
409    pub const fn has_explicit_event_admission(&self) -> bool {
410        self.event_admission_explicit
411    }
412}