Skip to main content

lenso_plugin_bundle/
model.rs

1use std::collections::BTreeMap;
2
3use lenso_app_plan::CapabilityOperationKind;
4use serde::{Deserialize, Serialize};
5
6/// Publisher-owned immutable Plugin Release manifest.
7#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
8#[serde(deny_unknown_fields)]
9pub struct PluginManifest {
10    pub schema_version: u32,
11    pub plugin_id: String,
12    pub release_version: String,
13    pub artifacts: Vec<ArtifactDeclaration>,
14    pub module_contributions: Vec<ModuleContribution>,
15    #[serde(default)]
16    pub data_contributions: Vec<DataContribution>,
17    #[serde(default)]
18    pub permission_requests: Vec<PermissionRequest>,
19    #[serde(default)]
20    pub features: Vec<PluginFeature>,
21    #[serde(default)]
22    pub binding_templates: Vec<BindingTemplate>,
23    #[serde(default)]
24    pub product_metadata: Vec<ProductMetadataDeclaration>,
25}
26
27/// Publisher-named optional set expanded before App Composition.
28#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
29#[serde(deny_unknown_fields)]
30pub struct PluginFeature {
31    pub id: String,
32    #[serde(default)]
33    pub module_contribution_ids: Vec<String>,
34    #[serde(default)]
35    pub data_contribution_ids: Vec<String>,
36    #[serde(default)]
37    pub artifact_ids: Vec<String>,
38    #[serde(default)]
39    pub permission_request_ids: Vec<String>,
40    #[serde(default)]
41    pub product_metadata_ids: Vec<String>,
42}
43
44/// Publisher-suggested binding constrained to contributions in this Release.
45#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
46#[serde(deny_unknown_fields)]
47pub struct BindingTemplate {
48    pub consumer_contribution_id: String,
49    pub provider_contribution_id: String,
50    pub capability_id: String,
51}
52
53/// Product-owned metadata bytes admitted generically but interpreted by the product.
54#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
55#[serde(deny_unknown_fields)]
56pub struct ProductMetadataDeclaration {
57    pub id: String,
58    pub namespace: String,
59    pub schema_id: String,
60    pub path: String,
61    pub digest: String,
62}
63
64/// One exact Artifact carried by a Plugin Release.
65#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
66#[serde(deny_unknown_fields)]
67pub struct ArtifactDeclaration {
68    pub id: String,
69    pub kind: ArtifactKind,
70    pub digest: String,
71    pub size: u64,
72    pub media_type: String,
73    pub path: String,
74    pub targets: Vec<String>,
75}
76
77/// Admitted Artifact interpretation.
78#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
79#[serde(rename_all = "snake_case")]
80pub enum ArtifactKind {
81    Process,
82    WasmComponent,
83    QuickJsModule,
84    NativeDylib,
85    Data,
86}
87
88/// One logical Module contribution with alternative execution implementations.
89#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
90#[serde(deny_unknown_fields)]
91pub struct ModuleContribution {
92    pub id: String,
93    pub package_id: String,
94    pub configuration_schema_digest: String,
95    pub provides: Vec<CapabilityDeclaration>,
96    #[serde(default)]
97    pub requires: Vec<CapabilityRequirement>,
98    pub implementations: Vec<ImplementationVariant>,
99    #[serde(default)]
100    pub permission_request_ids: Vec<String>,
101    #[serde(default)]
102    pub state: Option<StateDeclaration>,
103}
104
105/// Product-visible durable state identity owned by one logical Module contribution.
106#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
107#[serde(deny_unknown_fields)]
108pub struct StateDeclaration {
109    pub state_schema_id: String,
110    pub state_schema_digest: String,
111}
112
113/// Exact Capability Descriptor identity and Operation interaction table.
114#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
115#[serde(deny_unknown_fields)]
116pub struct CapabilityDeclaration {
117    pub capability_id: String,
118    pub descriptor_version: String,
119    pub descriptor_digest: String,
120    /// Historical field name for the complete ordered Operation table.
121    pub request_operations: Vec<String>,
122    /// Non-Request interaction kinds; omitted Operations default to Request.
123    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
124    pub operation_kinds: BTreeMap<String, CapabilityOperationKind>,
125}
126
127/// One required Capability identity and cardinality.
128#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
129#[serde(deny_unknown_fields)]
130pub struct CapabilityRequirement {
131    pub capability_id: String,
132    pub descriptor_version: String,
133    pub cardinality: RequirementCardinality,
134}
135
136/// Plan-supported dependency cardinality.
137#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
138#[serde(rename_all = "snake_case")]
139pub enum RequirementCardinality {
140    One,
141    Optional,
142    Many,
143}
144
145/// One target-specific implementation of a logical Module contribution.
146#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
147#[serde(deny_unknown_fields)]
148pub struct ImplementationVariant {
149    pub id: String,
150    pub artifact: Option<String>,
151    pub built_in_factory: Option<String>,
152    pub entrypoint: String,
153    pub execution_class: String,
154    pub targets: Vec<String>,
155    pub profiles: Vec<String>,
156    pub support_channel: SupportChannel,
157    pub trust: TrustLevel,
158}
159
160/// Product support status admitted by host policy.
161#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
162#[serde(rename_all = "snake_case")]
163pub enum SupportChannel {
164    Stable,
165    Preview,
166    Experimental,
167}
168
169/// Trust decision required by an execution implementation.
170#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
171#[serde(rename_all = "snake_case")]
172pub enum TrustLevel {
173    Constrained,
174    Isolated,
175    Trusted,
176}
177
178/// Inert Data contribution mounted into an explicit interpreter Instance.
179#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
180#[serde(deny_unknown_fields)]
181pub struct DataContribution {
182    pub id: String,
183    pub artifact: String,
184    pub media_type: String,
185    pub content_schema_id: String,
186    pub content_schema_digest: String,
187    pub product_metadata_digest: String,
188}
189
190/// Publisher request which is not itself host authority.
191#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
192#[serde(deny_unknown_fields)]
193pub struct PermissionRequest {
194    pub id: String,
195    pub resource_kind: String,
196    pub required: bool,
197    pub scope: serde_json::Value,
198    pub explanation_key: String,
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn legacy_request_capability_declaration_remains_canonical() {
207        let bytes = br#"{"capability_id":"example.echo@1","descriptor_version":"1.0.0","descriptor_digest":"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","request_operations":["echo"]}"#;
208        let declaration: CapabilityDeclaration = serde_json::from_slice(bytes).unwrap();
209
210        assert!(declaration.operation_kinds.is_empty());
211        assert_eq!(serde_json::to_vec(&declaration).unwrap(), bytes);
212    }
213}