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