Skip to main content

greentic_deploy_spec/
error.rs

1//! Cross-cutting error type for spec-level validators.
2
3use crate::capability_slot::CapabilitySlot;
4use crate::revision::RevisionLifecycle;
5use thiserror::Error;
6
7use crate::ids::{BundleId, DeploymentId, MessagingEndpointId, RevisionId};
8use greentic_types::EnvId;
9
10#[derive(Debug, Error, PartialEq, Eq)]
11pub enum SpecError {
12    #[error("basis-points entries must sum to 10000, got {sum}")]
13    BasisPointsSum { sum: u64 },
14
15    #[error("basis-points entry exceeds 10000: {value}")]
16    BasisPointsEntryTooLarge { value: u32 },
17
18    #[error("revenue-policy version must be >= 1 (1-based monotonic)")]
19    RevenuePolicyVersionZero,
20
21    #[error("duplicate capability slot `{0}` in Environment.packs")]
22    DuplicateCapabilitySlot(CapabilitySlot),
23
24    #[error("revision lifecycle transition {from:?} → {to:?} is not permitted")]
25    InvalidLifecycleTransition {
26        from: RevisionLifecycle,
27        to: RevisionLifecycle,
28    },
29
30    #[error("schema discriminator mismatch: expected `{expected}`, got `{actual}`")]
31    SchemaMismatch {
32        expected: &'static str,
33        actual: String,
34    },
35
36    #[error("env_id mismatch in {context}: expected `{expected}`, got `{actual}`")]
37    EnvIdMismatch {
38        context: &'static str,
39        expected: EnvId,
40        actual: EnvId,
41    },
42
43    #[error("traffic split references unknown deployment `{0}`")]
44    UnknownDeployment(DeploymentId),
45
46    #[error("reference to unknown revision `{0}`")]
47    UnknownRevision(RevisionId),
48
49    #[error(
50        "split entry revision `{revision}` belongs to deployment `{actual_deployment}`, not split's `{expected_deployment}`"
51    )]
52    SplitRevisionWrongDeployment {
53        revision: RevisionId,
54        expected_deployment: DeploymentId,
55        actual_deployment: DeploymentId,
56    },
57
58    #[error(
59        "split entry revision `{revision}` belongs to bundle `{actual_bundle}`, not split's `{expected_bundle}`"
60    )]
61    SplitRevisionWrongBundle {
62        revision: RevisionId,
63        expected_bundle: BundleId,
64        actual_bundle: BundleId,
65    },
66
67    #[error(
68        "bundle `{deployment}` current_revision `{revision}` belongs to deployment `{actual_deployment}`"
69    )]
70    BundleRevisionWrongDeployment {
71        deployment: DeploymentId,
72        revision: RevisionId,
73        actual_deployment: DeploymentId,
74    },
75
76    #[error(
77        "bundle deployment `{deployment}` current_revision `{revision}` belongs to bundle `{actual_bundle}`, not deployment's `{expected_bundle}`"
78    )]
79    BundleRevisionWrongBundle {
80        deployment: DeploymentId,
81        revision: RevisionId,
82        expected_bundle: BundleId,
83        actual_bundle: BundleId,
84    },
85
86    #[error(
87        "traffic split for deployment `{deployment}` carries bundle `{split_bundle}`, but the BundleDeployment record holds bundle `{deployment_bundle}`"
88    )]
89    SplitDeploymentBundleMismatch {
90        deployment: DeploymentId,
91        split_bundle: BundleId,
92        deployment_bundle: BundleId,
93    },
94
95    #[error("{context} ref `{uri}` is scoped to env `{actual_env}`, expected `{expected_env}`")]
96    CrossEnvRef {
97        context: &'static str,
98        uri: String,
99        expected_env: EnvId,
100        actual_env: String,
101    },
102
103    #[error("duplicate messaging endpoint id `{0}` in Environment.messaging_endpoints")]
104    DuplicateMessagingEndpoint(MessagingEndpointId),
105
106    #[error(
107        "duplicate provider instance `{provider_type}` / `{provider_id}` in Environment.messaging_endpoints"
108    )]
109    DuplicateProviderInstance {
110        provider_type: String,
111        provider_id: String,
112    },
113
114    #[error(
115        "messaging endpoint `{endpoint}` links bundle `{bundle}` which is not deployed in this env"
116    )]
117    MessagingEndpointBundleNotLinked {
118        endpoint: MessagingEndpointId,
119        bundle: BundleId,
120    },
121
122    #[error(
123        "messaging endpoint `{endpoint}` welcome_flow references bundle `{bundle}` which is not in linked_bundles"
124    )]
125    WelcomeFlowBundleNotLinked {
126        endpoint: MessagingEndpointId,
127        bundle: BundleId,
128    },
129
130    #[error("messaging endpoint provider_id is empty")]
131    EmptyMessagingProviderId,
132
133    #[error("messaging endpoint provider_type is empty")]
134    EmptyMessagingProviderType,
135
136    #[error("messaging endpoint welcome_flow.flow_id is empty")]
137    EmptyWelcomeFlowId,
138
139    #[error(
140        "duplicate extension binding for path `{path}` / instance `{instance_id:?}` in Environment.extensions"
141    )]
142    DuplicateExtension {
143        path: String,
144        instance_id: Option<String>,
145    },
146
147    #[error("extension binding `{path}` has an invalid instance id: {reason}")]
148    InvalidExtensionInstanceId { path: String, reason: String },
149
150    #[error("bundle config_overrides carries {count} packs, exceeds cap of {max}")]
151    ConfigOverridesTooManyPacks { count: usize, max: usize },
152
153    #[error(
154        "bundle config_overrides for pack `{pack_id}` carries {count} keys, exceeds cap of {max}"
155    )]
156    ConfigOverridesTooManyKeysForPack {
157        pack_id: String,
158        count: usize,
159        max: usize,
160    },
161
162    #[error("bundle config_overrides serialized size is {bytes} bytes, exceeds cap of {max}")]
163    ConfigOverridesTooLarge { bytes: usize, max: usize },
164
165    #[error("bundle config_overrides has an empty pack id key")]
166    ConfigOverrideEmptyPackId,
167
168    #[error("bundle config_overrides for pack `{pack_id}` has an empty config key")]
169    ConfigOverrideEmptyKey { pack_id: String },
170
171    #[error(
172        "bundle deployment `{deployment}` config_overrides references pack `{pack_id}` which is not in any current revision's pack_list"
173    )]
174    ConfigOverridePackNotInRevisions {
175        deployment: DeploymentId,
176        pack_id: String,
177    },
178
179    #[error("public_base_url `{value}` is invalid: {reason}")]
180    InvalidPublicBaseUrl { value: String, reason: &'static str },
181}