1use std::{fmt, time::Duration};
2
3use super::CapabilityOperationKind;
4
5#[derive(Clone, Debug, Eq, PartialEq)]
7pub enum PlanResolutionError {
8 UnsupportedTerminalPolicy,
10 InvalidTerminalPolicy { detail: String },
12 InvalidAuthoring { instance_key: String },
14 InvalidRequirementId {
16 consumer_instance: String,
17 requirement_id: String,
18 },
19 DuplicateRequirementId {
21 consumer_instance: String,
22 requirement_id: String,
23 },
24 UnsupportedSchemaVersion { expected: u32, actual: u32 },
26 DuplicatePluginInstance { instance_key: String },
28 MissingExecutionLane,
30 InvalidExecutionLane { execution_lane: String },
32 DuplicateExecutionLane { execution_lane: String },
34 UndeclaredExecutionLane {
36 instance_key: String,
37 execution_lane: String,
38 },
39 InvalidPluginEntrypoint { instance_key: String },
41 DuplicateProvidedCapability {
43 provider_instance: String,
44 capability_id: String,
45 },
46 DuplicateOperation {
48 provider_instance: String,
49 capability_id: String,
50 operation: String,
51 },
52 DuplicateRequiredCapability {
54 consumer_instance: String,
55 capability_id: String,
56 },
57 InvalidConsumerReference {
59 consumer_instance: String,
60 capability_id: String,
61 },
62 InvalidProviderReference {
64 consumer_instance: String,
65 capability_id: String,
66 provider_instance: String,
67 },
68 UndeclaredCapabilityRequirement {
70 consumer_instance: String,
71 capability_id: String,
72 },
73 IncompatibleCapabilityVersion {
75 consumer_instance: String,
76 capability_id: String,
77 required: String,
78 provided: String,
79 provider_instance: String,
80 },
81 CrossLaneTransferUnsupported {
83 consumer_instance: String,
84 provider_instance: String,
85 capability_id: String,
86 },
87 CrossLaneInteractionUnsupported {
91 capability_id: String,
92 operation: String,
93 interaction: CapabilityOperationKind,
94 },
95 MissingOneBinding {
97 consumer_instance: String,
98 capability_id: String,
99 },
100 AmbiguousOneBinding {
102 consumer_instance: String,
103 capability_id: String,
104 providers: usize,
105 },
106 AmbiguousOptionalBinding {
108 consumer_instance: String,
109 capability_id: String,
110 providers: usize,
111 },
112 DuplicateBinding {
114 consumer_instance: String,
115 capability_id: String,
116 provider_instance: String,
117 },
118 InvalidRequestAdmission {
120 capability_id: String,
121 operation: String,
122 queue_capacity: usize,
123 max_concurrency: usize,
124 },
125 UnknownAdmissionOperation {
127 capability_id: String,
128 operation: String,
129 },
130 UnknownOperationInteraction {
132 capability_id: String,
133 operation: String,
134 },
135 InvalidRestartPolicy {
137 instance_key: String,
138 max_attempts: usize,
139 window: Duration,
140 },
141 ActivationCycle { instances: Vec<String> },
143}
144
145impl fmt::Display for PlanResolutionError {
146 #[allow(clippy::too_many_lines)]
147 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
148 match self {
149 Self::UnsupportedTerminalPolicy => {
150 formatter.write_str("unsupported host_essential terminal policy")
151 }
152 Self::InvalidTerminalPolicy { detail } => {
153 write!(formatter, "invalid terminal policy: {detail}")
154 }
155 Self::InvalidAuthoring { instance_key } => write!(
156 formatter,
157 "invalid authoring version or runtime profile for `{instance_key}`"
158 ),
159 Self::InvalidRequirementId {
160 consumer_instance,
161 requirement_id,
162 } => write!(
163 formatter,
164 "invalid requirement `{requirement_id}` for `{consumer_instance}`"
165 ),
166 Self::DuplicateRequirementId {
167 consumer_instance,
168 requirement_id,
169 } => write!(
170 formatter,
171 "duplicate requirement `{requirement_id}` for `{consumer_instance}`"
172 ),
173 Self::UnsupportedSchemaVersion { expected, actual } => write!(
174 formatter,
175 "unsupported Plan schema version {actual}; expected {expected}"
176 ),
177 Self::DuplicatePluginInstance { instance_key } => {
178 write!(formatter, "duplicate Plugin Instance `{instance_key}`")
179 }
180 Self::MissingExecutionLane => {
181 formatter.write_str("Resolved App Plan declares no Execution Lanes")
182 }
183 Self::InvalidExecutionLane { execution_lane } => {
184 write!(formatter, "invalid Execution Lane `{execution_lane}`")
185 }
186 Self::DuplicateExecutionLane { execution_lane } => {
187 write!(formatter, "duplicate Execution Lane `{execution_lane}`")
188 }
189 Self::UndeclaredExecutionLane {
190 instance_key,
191 execution_lane,
192 } => write!(
193 formatter,
194 "Plugin Instance `{instance_key}` is placed on undeclared Execution Lane `{execution_lane}`"
195 ),
196 Self::InvalidPluginEntrypoint { instance_key } => write!(
197 formatter,
198 "Plugin Instance `{instance_key}` has an empty entrypoint"
199 ),
200 Self::DuplicateProvidedCapability {
201 provider_instance,
202 capability_id,
203 } => write!(
204 formatter,
205 "Plugin Instance `{provider_instance}` provides Capability `{capability_id}` more than once"
206 ),
207 Self::DuplicateOperation {
208 provider_instance,
209 capability_id,
210 operation,
211 } => write!(
212 formatter,
213 "Plugin Instance `{provider_instance}` Capability `{capability_id}` declares Operation `{operation}` more than once"
214 ),
215 Self::DuplicateRequiredCapability {
216 consumer_instance,
217 capability_id,
218 } => write!(
219 formatter,
220 "Plugin Instance `{consumer_instance}` requires Capability `{capability_id}` more than once"
221 ),
222 Self::InvalidConsumerReference {
223 consumer_instance,
224 capability_id,
225 } => write!(
226 formatter,
227 "Capability `{capability_id}` names missing consumer `{consumer_instance}`"
228 ),
229 Self::InvalidProviderReference {
230 consumer_instance,
231 capability_id,
232 provider_instance,
233 } => write!(
234 formatter,
235 "consumer `{consumer_instance}` names invalid provider `{provider_instance}` for Capability `{capability_id}`"
236 ),
237 Self::UndeclaredCapabilityRequirement {
238 consumer_instance,
239 capability_id,
240 } => write!(
241 formatter,
242 "consumer `{consumer_instance}` has no declared requirement for Capability `{capability_id}`"
243 ),
244 Self::IncompatibleCapabilityVersion {
245 consumer_instance,
246 capability_id,
247 required,
248 provided,
249 provider_instance,
250 } => write!(
251 formatter,
252 "consumer `{consumer_instance}` requires Capability `{capability_id}` version `{required}`, but provider `{provider_instance}` provides `{provided}`"
253 ),
254 Self::CrossLaneTransferUnsupported {
255 consumer_instance,
256 provider_instance,
257 capability_id,
258 } => write!(
259 formatter,
260 "consumer `{consumer_instance}` binds Capability `{capability_id}` across Execution Lanes to provider `{provider_instance}`, but its contract types do not support cross-lane transfer"
261 ),
262 Self::CrossLaneInteractionUnsupported {
263 capability_id,
264 operation,
265 interaction,
266 } => write!(
267 formatter,
268 "Capability `{capability_id}` Operation `{operation}` uses {interaction:?}, which this Plan version cannot transfer across Execution Lanes"
269 ),
270 Self::MissingOneBinding {
271 consumer_instance,
272 capability_id,
273 } => write!(
274 formatter,
275 "consumer `{consumer_instance}` is missing one binding for Capability `{capability_id}`"
276 ),
277 Self::AmbiguousOneBinding {
278 consumer_instance,
279 capability_id,
280 providers,
281 } => write!(
282 formatter,
283 "consumer `{consumer_instance}` has {providers} bindings for one Capability `{capability_id}`"
284 ),
285 Self::AmbiguousOptionalBinding {
286 consumer_instance,
287 capability_id,
288 providers,
289 } => write!(
290 formatter,
291 "consumer `{consumer_instance}` has {providers} bindings for optional Capability `{capability_id}`"
292 ),
293 Self::DuplicateBinding {
294 consumer_instance,
295 capability_id,
296 provider_instance,
297 } => write!(
298 formatter,
299 "consumer `{consumer_instance}` binds Capability `{capability_id}` to provider `{provider_instance}` more than once"
300 ),
301 Self::InvalidRequestAdmission {
302 capability_id,
303 operation,
304 queue_capacity,
305 max_concurrency,
306 } => write!(
307 formatter,
308 "Capability `{capability_id}` Operation `{operation}` has invalid request admission (queue capacity {queue_capacity}, concurrency {max_concurrency})"
309 ),
310 Self::UnknownAdmissionOperation {
311 capability_id,
312 operation,
313 } => write!(
314 formatter,
315 "Capability `{capability_id}` configures request admission for unknown Operation `{operation}`"
316 ),
317 Self::UnknownOperationInteraction {
318 capability_id,
319 operation,
320 } => write!(
321 formatter,
322 "Capability `{capability_id}` configures interaction metadata for unknown Operation `{operation}`"
323 ),
324 Self::InvalidRestartPolicy {
325 instance_key,
326 max_attempts,
327 window,
328 } => write!(
329 formatter,
330 "Plugin Instance `{instance_key}` has invalid restart policy (attempts {max_attempts}, window {window:?})"
331 ),
332 Self::ActivationCycle { instances } => write!(
333 formatter,
334 "required Capability activation cycle: {}",
335 instances.join(" -> ")
336 ),
337 }
338 }
339}
340
341impl std::error::Error for PlanResolutionError {}