1use std::collections::BTreeMap;
2
3use lenso_app_plan::CapabilityOperationKind;
4use serde::{Deserialize, Serialize};
5
6#[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#[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#[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#[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#[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#[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#[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#[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#[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 pub request_operations: Vec<String>,
122 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
124 pub operation_kinds: BTreeMap<String, CapabilityOperationKind>,
125}
126
127#[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#[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#[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#[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#[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#[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#[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}