1use std::{borrow::Cow, collections::BTreeMap, fmt};
2
3use jiff::Timestamp;
4use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
5use serde::{Deserialize, Serialize};
6use zeroize::Zeroize;
7
8use crate::*;
9
10const MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991;
11
12macro_rules! positive_counter {
13 ($name:ident, $description:literal) => {
14 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
15 #[serde(transparent)]
16 pub struct $name(u64);
17
18 impl $name {
19 pub fn new(value: u64) -> Result<Self, ProviderSchemaError> {
20 if (1..=MAX_SAFE_INTEGER).contains(&value) {
21 Ok(Self(value))
22 } else {
23 Err(ProviderSchemaError::InvalidCounter)
24 }
25 }
26
27 #[must_use]
28 pub const fn get(self) -> u64 { self.0 }
29 }
30
31 impl<'de> Deserialize<'de> for $name {
32 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
33 where D: serde::Deserializer<'de> {
34 Self::new(u64::deserialize(deserializer)?).map_err(serde::de::Error::custom)
35 }
36 }
37
38 impl JsonSchema for $name {
39 fn inline_schema() -> bool { true }
40 fn schema_name() -> Cow<'static, str> { Cow::Borrowed(stringify!($name)) }
41 fn json_schema(_: &mut SchemaGenerator) -> Schema {
42 json_schema!({"type":"integer","minimum":1,"maximum":MAX_SAFE_INTEGER,"description":$description})
43 }
44 }
45 };
46}
47
48positive_counter!(
49 ProviderStoreGeneration,
50 "Monotonic provider-store generation."
51);
52positive_counter!(
53 ProviderConnectionGeneration,
54 "Monotonic provider connection generation."
55);
56
57#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
58pub struct BoundedSetupString(String);
59
60impl BoundedSetupString {
61 pub const MAX_BYTES: usize = 2048;
62 pub fn new(value: impl Into<String>) -> Result<Self, ProviderSchemaError> {
63 let value = value.into();
64 if value.is_empty() || value.len() > Self::MAX_BYTES || value.chars().any(char::is_control)
65 {
66 Err(ProviderSchemaError::InvalidSetupString)
67 } else {
68 Ok(Self(value))
69 }
70 }
71 #[must_use]
72 pub fn as_str(&self) -> &str {
73 &self.0
74 }
75}
76impl Serialize for BoundedSetupString {
77 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
78 where
79 S: serde::Serializer,
80 {
81 serializer.serialize_str(&self.0)
82 }
83}
84impl<'de> Deserialize<'de> for BoundedSetupString {
85 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
86 where
87 D: serde::Deserializer<'de>,
88 {
89 Self::new(String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
90 }
91}
92impl JsonSchema for BoundedSetupString {
93 fn inline_schema() -> bool {
94 true
95 }
96 fn schema_name() -> Cow<'static, str> {
97 Cow::Borrowed("BoundedSetupString")
98 }
99 fn json_schema(_: &mut SchemaGenerator) -> Schema {
100 json_schema!({"type":"string","minLength":1,"maxLength":2048,"pattern":"^[^\\p{Cc}\\p{Cf}]+$"})
101 }
102}
103
104#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
105#[serde(untagged)]
106pub enum SafeSetupValue {
107 Bool(bool),
108 Integer(i64),
109 Code(SafeCode),
110 String(BoundedSetupString),
111}
112
113#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
114#[serde(rename_all = "snake_case")]
115pub enum SetupFieldType {
116 String,
117 Code,
118 Integer,
119 Bool,
120}
121
122#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
123#[serde(rename_all = "snake_case")]
124pub enum CredentialFieldType {
125 ApiKey,
126 AccessToken,
127 AccessKeyId,
128 SecretAccessKey,
129 SessionToken,
130}
131
132#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
133#[serde(deny_unknown_fields)]
134pub struct SetupFieldValidation {
135 pub value_type: SetupFieldType,
136 #[serde(deserialize_with = "crate::deserialize_required_option")]
137 #[schemars(with = "crate::NullableSchema<u32>", required)]
138 pub min_length: Option<u32>,
139 #[serde(deserialize_with = "crate::deserialize_required_option")]
140 #[schemars(with = "crate::NullableSchema<u32>", required)]
141 pub max_length: Option<u32>,
142 #[serde(deserialize_with = "crate::deserialize_required_option")]
143 #[schemars(with = "crate::NullableSchema<i64>", required)]
144 pub minimum: Option<i64>,
145 #[serde(deserialize_with = "crate::deserialize_required_option")]
146 #[schemars(with = "crate::NullableSchema<i64>", required)]
147 pub maximum: Option<i64>,
148}
149
150#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
151#[serde(deny_unknown_fields)]
152pub struct SetupFieldDescriptor {
153 pub id: SetupFieldId,
154 pub display_name: SafeDisplayText,
155 pub help: SafeDisplayText,
156 pub required: bool,
157 #[serde(deserialize_with = "crate::deserialize_required_option")]
158 #[schemars(with = "crate::NullableSchema<SafeSetupValue>", required)]
159 pub default: Option<SafeSetupValue>,
160 pub validation: SetupFieldValidation,
161 pub safe_to_project: bool,
162}
163
164#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
165#[serde(deny_unknown_fields)]
166pub struct AuthCredentialDescriptor {
167 pub id: AuthFieldName,
168 pub display_name: SafeDisplayText,
169 pub help: SafeDisplayText,
170 pub required: bool,
171 pub credential_type: CredentialFieldType,
172}
173
174#[derive(Clone, Debug, Eq, JsonSchema, PartialEq, Serialize)]
175#[serde(deny_unknown_fields)]
176pub struct AuthMethodDescriptor {
177 pub id: AuthMethodId,
178 pub display_name: SafeDisplayText,
179 #[schemars(length(min = 1, max = 32))]
180 pub credentials: Vec<AuthCredentialDescriptor>,
181}
182impl<'de> Deserialize<'de> for AuthMethodDescriptor {
183 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
184 where
185 D: serde::Deserializer<'de>,
186 {
187 #[derive(Deserialize)]
188 #[serde(deny_unknown_fields)]
189 struct Wire {
190 id: AuthMethodId,
191 display_name: SafeDisplayText,
192 credentials: Vec<AuthCredentialDescriptor>,
193 }
194 let wire = Wire::deserialize(deserializer)?;
195 if wire.credentials.is_empty()
196 || wire.credentials.len() > 32
197 || wire
198 .credentials
199 .windows(2)
200 .any(|pair| pair[0].id >= pair[1].id)
201 {
202 return Err(serde::de::Error::custom(
203 "auth credentials must be a sorted unique list of 1..=32 fields",
204 ));
205 }
206 Ok(Self {
207 id: wire.id,
208 display_name: wire.display_name,
209 credentials: wire.credentials,
210 })
211 }
212}
213
214#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
215#[serde(rename_all = "snake_case")]
216pub enum ProviderPresence {
217 Current,
218 Removed,
219}
220
221#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
222#[serde(rename_all = "snake_case")]
223pub enum ProviderSupportState {
224 Supported,
225 Unsupported,
226 Quarantined,
227}
228
229#[derive(Clone, Debug, Eq, JsonSchema, PartialEq, Serialize)]
230#[serde(deny_unknown_fields)]
231pub struct ProviderSupport {
232 pub state: ProviderSupportState,
233 #[serde(deserialize_with = "crate::deserialize_required_option")]
234 #[schemars(with = "crate::NullableSchema<SafeCode>", required)]
235 pub reason: Option<SafeCode>,
236}
237impl<'de> Deserialize<'de> for ProviderSupport {
238 fn deserialize<D>(d: D) -> Result<Self, D::Error>
239 where
240 D: serde::Deserializer<'de>,
241 {
242 #[derive(Deserialize)]
243 #[serde(deny_unknown_fields)]
244 struct Wire {
245 state: ProviderSupportState,
246 #[serde(deserialize_with = "crate::deserialize_required_option")]
247 reason: Option<SafeCode>,
248 }
249 let wire = Wire::deserialize(d)?;
250 if (wire.state == ProviderSupportState::Supported) != wire.reason.is_none() {
251 return Err(serde::de::Error::custom(
252 "supported providers have no reason; unsupported and quarantined providers require one",
253 ));
254 }
255 Ok(Self {
256 state: wire.state,
257 reason: wire.reason,
258 })
259 }
260}
261
262#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
263#[serde(deny_unknown_fields)]
264pub struct QuarantineDiagnostic {
265 pub code: SafeCode,
266 pub message: SafeErrorMessage,
267}
268
269#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
270#[serde(rename_all = "snake_case")]
271pub enum ProviderConfigurationState {
272 Unconfigured,
273 Authored,
274 Stored,
275 AuthoredAndStored,
276}
277
278#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
279#[serde(rename_all = "snake_case")]
280pub enum EffectiveAuthSource {
281 AuthoredApiKey,
282 AuthoredOverride,
283 ProviderStore,
284 NoAuth,
285}
286
287#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
288#[serde(rename_all = "snake_case")]
289pub enum EffectiveAuthState {
290 AuthoredApiKey,
291 AuthoredOverride,
292 ProviderStore,
293 NoAuth,
294 Unavailable,
295}
296
297#[derive(Clone, Debug, Eq, JsonSchema, PartialEq, Serialize)]
298#[serde(deny_unknown_fields)]
299pub struct DurableConnectionDescriptor {
300 pub provider_id: ProviderId,
301 pub setup_values: BTreeMap<SetupFieldId, SafeSetupValue>,
302 pub setup_fingerprint: Sha256Digest,
303 pub recipe_fingerprint: Sha256Digest,
304 pub auth_method: AuthMethodId,
305 #[schemars(length(max = 32))]
306 pub credential_fields: Vec<AuthFieldName>,
307 pub connection_generation: ProviderConnectionGeneration,
308 pub connected_at: Timestamp,
309}
310impl<'de> Deserialize<'de> for DurableConnectionDescriptor {
311 fn deserialize<D>(d: D) -> Result<Self, D::Error>
312 where
313 D: serde::Deserializer<'de>,
314 {
315 #[derive(Deserialize)]
316 #[serde(deny_unknown_fields)]
317 struct Wire {
318 provider_id: ProviderId,
319 setup_values: BTreeMap<SetupFieldId, SafeSetupValue>,
320 setup_fingerprint: Sha256Digest,
321 recipe_fingerprint: Sha256Digest,
322 auth_method: AuthMethodId,
323 credential_fields: Vec<AuthFieldName>,
324 connection_generation: ProviderConnectionGeneration,
325 connected_at: Timestamp,
326 }
327 let wire = Wire::deserialize(d)?;
328 if wire.setup_values.len() > 32
329 || wire.credential_fields.len() > 32
330 || wire
331 .credential_fields
332 .windows(2)
333 .any(|pair| pair[0] >= pair[1])
334 {
335 return Err(serde::de::Error::custom(
336 "durable connection setup and credential metadata are not bounded and sorted",
337 ));
338 }
339 Ok(Self {
340 provider_id: wire.provider_id,
341 setup_values: wire.setup_values,
342 setup_fingerprint: wire.setup_fingerprint,
343 recipe_fingerprint: wire.recipe_fingerprint,
344 auth_method: wire.auth_method,
345 credential_fields: wire.credential_fields,
346 connection_generation: wire.connection_generation,
347 connected_at: wire.connected_at,
348 })
349 }
350}
351
352#[derive(Clone, Debug, Eq, JsonSchema, PartialEq, Serialize)]
353#[serde(deny_unknown_fields)]
354pub struct ProviderDescriptor {
355 pub id: ProviderId,
356 pub display_name: SafeDisplayText,
357 pub presence: ProviderPresence,
358 pub support: ProviderSupport,
359 pub setup_fields: Vec<SetupFieldDescriptor>,
360 pub auth_methods: Vec<AuthMethodDescriptor>,
361 pub configuration: ProviderConfigurationState,
362 pub effective_auth_state: EffectiveAuthState,
363 #[serde(deserialize_with = "crate::deserialize_required_option")]
364 #[schemars(with = "crate::NullableSchema<DurableConnectionDescriptor>", required)]
365 pub durable_connection: Option<DurableConnectionDescriptor>,
366 #[serde(deserialize_with = "crate::deserialize_required_option")]
367 #[schemars(with = "crate::NullableSchema<QuarantineDiagnostic>", required)]
368 pub quarantine: Option<QuarantineDiagnostic>,
369}
370impl<'de> Deserialize<'de> for ProviderDescriptor {
371 fn deserialize<D>(d: D) -> Result<Self, D::Error>
372 where
373 D: serde::Deserializer<'de>,
374 {
375 #[derive(Deserialize)]
376 #[serde(deny_unknown_fields)]
377 struct Wire {
378 id: ProviderId,
379 display_name: SafeDisplayText,
380 presence: ProviderPresence,
381 support: ProviderSupport,
382 setup_fields: Vec<SetupFieldDescriptor>,
383 auth_methods: Vec<AuthMethodDescriptor>,
384 configuration: ProviderConfigurationState,
385 effective_auth_state: EffectiveAuthState,
386 #[serde(deserialize_with = "crate::deserialize_required_option")]
387 durable_connection: Option<DurableConnectionDescriptor>,
388 #[serde(deserialize_with = "crate::deserialize_required_option")]
389 quarantine: Option<QuarantineDiagnostic>,
390 }
391 let wire = Wire::deserialize(d)?;
392 if wire.setup_fields.len() > 32
393 || wire
394 .setup_fields
395 .windows(2)
396 .any(|pair| pair[0].id >= pair[1].id)
397 || wire.auth_methods.len() > 16
398 || wire
399 .auth_methods
400 .windows(2)
401 .any(|pair| pair[0].id >= pair[1].id)
402 || (wire.support.state == ProviderSupportState::Quarantined)
403 != wire.quarantine.is_some()
404 || wire
405 .durable_connection
406 .as_ref()
407 .is_some_and(|connection| connection.provider_id != wire.id)
408 {
409 return Err(serde::de::Error::custom(
410 "provider descriptor metadata is inconsistent or not strictly sorted",
411 ));
412 }
413 Ok(Self {
414 id: wire.id,
415 display_name: wire.display_name,
416 presence: wire.presence,
417 support: wire.support,
418 setup_fields: wire.setup_fields,
419 auth_methods: wire.auth_methods,
420 configuration: wire.configuration,
421 effective_auth_state: wire.effective_auth_state,
422 durable_connection: wire.durable_connection,
423 quarantine: wire.quarantine,
424 })
425 }
426}
427
428#[derive(Clone, Eq, PartialEq)]
429pub struct ProviderCredentialValues(BTreeMap<String, String>);
430
431impl ProviderCredentialValues {
432 #[must_use]
433 pub fn get(&self, field: &AuthFieldName) -> Option<&str> {
434 self.0.get(field.as_str()).map(String::as_str)
435 }
436 pub fn field_names(&self) -> impl Iterator<Item = &str> {
437 self.0.keys().map(String::as_str)
438 }
439}
440impl fmt::Debug for ProviderCredentialValues {
441 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
442 formatter.write_str("ProviderCredentialValues(<redacted>)")
443 }
444}
445impl Drop for ProviderCredentialValues {
446 fn drop(&mut self) {
447 for value in self.0.values_mut() {
448 value.zeroize();
449 }
450 }
451}
452impl<'de> Deserialize<'de> for ProviderCredentialValues {
453 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
454 where
455 D: serde::Deserializer<'de>,
456 {
457 struct Visitor;
458 impl<'de> serde::de::Visitor<'de> for Visitor {
459 type Value = BTreeMap<String, String>;
460 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
461 formatter.write_str("a unique credential field map")
462 }
463 fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
464 where
465 A: serde::de::MapAccess<'de>,
466 {
467 let mut raw = BTreeMap::new();
468 while let Some((key, value)) = access.next_entry::<String, String>()? {
469 if raw.len() >= 32 {
470 return Err(serde::de::Error::custom("auth values exceed 32 fields"));
471 }
472 if raw.insert(key, value).is_some() {
473 return Err(serde::de::Error::custom("duplicate auth credential field"));
474 }
475 }
476 Ok(raw)
477 }
478 }
479 let raw = deserializer.deserialize_map(Visitor)?;
480 if raw.len() > 32
481 || raw.iter().any(|(key, value)| {
482 AuthFieldName::new(key.clone()).is_err()
483 || value.is_empty()
484 || value.len() > 16 * 1024
485 })
486 {
487 return Err(serde::de::Error::custom(
488 "auth values must contain at most 32 strict bounded nonempty credential fields",
489 ));
490 }
491 Ok(Self(raw))
492 }
493}
494
495#[derive(Clone, Debug, Eq, PartialEq)]
496pub struct ProviderConnectParams {
497 pub provider_id: ProviderId,
498 pub expected_catalog_revision: CatalogRevision,
499 pub setup_values: BTreeMap<SetupFieldId, SafeSetupValue>,
500 pub auth_method: AuthMethodId,
501 pub auth_values: ProviderCredentialValues,
502 pub client_connect_id: ClientConnectId,
503}
504
505impl ProviderConnectParams {
506 pub fn validate(&self) -> Result<(), ProviderSchemaError> {
507 if self.setup_values.len() > 32 {
508 Err(ProviderSchemaError::TooManySetupFields)
509 } else {
510 Ok(())
511 }
512 }
513}
514impl<'de> Deserialize<'de> for ProviderConnectParams {
515 fn deserialize<D>(d: D) -> Result<Self, D::Error>
516 where
517 D: serde::Deserializer<'de>,
518 {
519 #[derive(Deserialize)]
520 #[serde(deny_unknown_fields)]
521 struct Wire {
522 provider_id: ProviderId,
523 expected_catalog_revision: CatalogRevision,
524 #[serde(deserialize_with = "deserialize_setup_values")]
525 setup_values: BTreeMap<SetupFieldId, SafeSetupValue>,
526 auth_method: AuthMethodId,
527 auth_values: ProviderCredentialValues,
528 client_connect_id: ClientConnectId,
529 }
530 let w = Wire::deserialize(d)?;
531 let value = Self {
532 provider_id: w.provider_id,
533 expected_catalog_revision: w.expected_catalog_revision,
534 setup_values: w.setup_values,
535 auth_method: w.auth_method,
536 auth_values: w.auth_values,
537 client_connect_id: w.client_connect_id,
538 };
539 value.validate().map_err(serde::de::Error::custom)?;
540 Ok(value)
541 }
542}
543
544fn deserialize_setup_values<'de, D>(
545 d: D,
546) -> Result<BTreeMap<SetupFieldId, SafeSetupValue>, D::Error>
547where
548 D: serde::Deserializer<'de>,
549{
550 struct Visitor;
551 impl<'de> serde::de::Visitor<'de> for Visitor {
552 type Value = BTreeMap<SetupFieldId, SafeSetupValue>;
553 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
554 f.write_str("a unique bounded setup field map")
555 }
556 fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
557 where
558 A: serde::de::MapAccess<'de>,
559 {
560 let mut values = BTreeMap::new();
561 while let Some((key, value)) = access.next_entry::<SetupFieldId, SafeSetupValue>()? {
562 if values.len() >= 32 {
563 return Err(serde::de::Error::custom("setup values exceed 32 fields"));
564 }
565 if values.insert(key, value).is_some() {
566 return Err(serde::de::Error::custom("duplicate setup field"));
567 }
568 }
569 Ok(values)
570 }
571 }
572 d.deserialize_map(Visitor)
573}
574
575#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
576#[serde(deny_unknown_fields)]
577pub struct DurableProviderReceipt {
578 pub receipt_id: DurableProviderReceiptId,
579 pub store_revision: ProviderStoreRevision,
580 pub provider_state_revision: ProviderStateRevision,
581 pub committed_at: Timestamp,
582}
583
584#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
585#[serde(deny_unknown_fields)]
586pub struct ProviderConnectResult {
587 pub durable_connection: DurableConnectionDescriptor,
588 pub effective_auth_source: EffectiveAuthSource,
589 pub runtime: RuntimeSnapshotV1,
590 pub replayed: bool,
591}
592
593#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
594#[serde(deny_unknown_fields)]
595pub struct ProviderDisconnectParams {
596 pub provider_id: ProviderId,
597 pub expected_runtime_revision: RuntimeRevision,
598 pub expected_provider_state_revision: ProviderStateRevision,
599 #[serde(deserialize_with = "crate::deserialize_required_option")]
600 #[schemars(with = "crate::NullableSchema<ProviderConnectionGeneration>", required)]
601 pub expected_connection_generation: Option<ProviderConnectionGeneration>,
602 pub client_request_id: ClientRequestId,
603}
604
605#[derive(Clone, Debug, Eq, JsonSchema, PartialEq, Serialize)]
606#[serde(deny_unknown_fields)]
607pub struct ProviderDisconnectResult {
608 pub durable_receipt: DurableProviderReceipt,
609 pub provider_id: ProviderId,
610 pub disconnected: bool,
611 pub effective_auth_state: EffectiveAuthState,
612 pub runtime: RuntimeSnapshotResult,
613 pub replayed: bool,
614}
615impl<'de> Deserialize<'de> for ProviderDisconnectResult {
616 fn deserialize<D>(d: D) -> Result<Self, D::Error>
617 where
618 D: serde::Deserializer<'de>,
619 {
620 #[derive(Deserialize)]
621 #[serde(deny_unknown_fields)]
622 struct Wire {
623 durable_receipt: DurableProviderReceipt,
624 provider_id: ProviderId,
625 disconnected: bool,
626 effective_auth_state: EffectiveAuthState,
627 runtime: RuntimeSnapshotResult,
628 replayed: bool,
629 }
630 let w = Wire::deserialize(d)?;
631 if !w.disconnected {
632 return Err(serde::de::Error::custom(
633 "disconnected must be true on success",
634 ));
635 }
636 Ok(Self {
637 durable_receipt: w.durable_receipt,
638 provider_id: w.provider_id,
639 disconnected: w.disconnected,
640 effective_auth_state: w.effective_auth_state,
641 runtime: w.runtime,
642 replayed: w.replayed,
643 })
644 }
645}
646
647#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
648#[serde(rename_all = "snake_case")]
649pub enum ProviderConnectErrorCode {
650 UnknownProvider,
651 UnsupportedProvider,
652 QuarantinedProvider,
653 RemovedWithoutRetainedRecipeMatch,
654 CatalogRevisionConflict,
655 MissingSetupField,
656 InvalidSetupField,
657 MissingCredential,
658 InvalidCredential,
659 UnsupportedAuthMethod,
660 ProviderStoreWriteFailed,
661 LockContention,
662 RuntimeCompileFailed,
663 IdempotencyConflict,
664}
665
666#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
667#[serde(deny_unknown_fields)]
668pub struct ProviderConnectError {
669 pub code: ProviderConnectErrorCode,
670 pub provider_id: ProviderId,
671 pub client_connect_id: ClientConnectId,
672}
673
674#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
675#[serde(rename_all = "snake_case")]
676pub enum ProviderDisconnectErrorCode {
677 InvalidProvider,
678 CustomProviderNotStoreBacked,
679 RuntimeRevisionConflict,
680 ProviderStateRevisionConflict,
681 StaleProviderConnectionGeneration,
682 ProviderStoreWriteFailed,
683 LockContention,
684 RuntimeCompileFailed,
685 IdempotencyConflict,
686}
687
688#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
689#[serde(deny_unknown_fields)]
690pub struct ProviderDisconnectError {
691 pub code: ProviderDisconnectErrorCode,
692 pub provider_id: ProviderId,
693 pub client_request_id: ClientRequestId,
694}
695
696#[derive(Clone, Copy, Debug, Eq, PartialEq)]
697pub enum ProviderSchemaError {
698 InvalidCounter,
699 InvalidSetupString,
700 TooManySetupFields,
701}
702impl fmt::Display for ProviderSchemaError {
703 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
704 formatter.write_str(match self {
705 Self::InvalidCounter => "counter must be positive and I-JSON safe",
706 Self::InvalidSetupString => "setup string must be control-free and 1..=2048 bytes",
707 Self::TooManySetupFields => "setup values exceed 32 fields",
708 })
709 }
710}
711impl std::error::Error for ProviderSchemaError {}