1use std::fmt::Display;
4
5use reqwest::{Error as HttpError, StatusCode, Url};
6use spiral_contract::call::CallError;
7use uuid::Uuid;
8
9use crate::api::apps::AppsError;
10use crate::api::authn::AuthnError;
11use crate::api::authorization::AuthorizationError;
12use crate::api::catalog::{CatalogError, PublicationConflict};
13use crate::api::connections::ConnectionsError;
14use crate::api::enrichments::EnrichmentsError;
15use crate::api::locations::LocationsError;
16use crate::api::organization::OrganizationError;
17use crate::api::project::ProjectError;
18use crate::api::storage::StorageError;
19use crate::api::tables::TablesError;
20use crate::api::tasks::TasksError;
21use crate::api::usage::UsageError;
22use crate::api::vector_indexes::VectorIndexesError;
23use crate::api::workers::WorkersError;
24use crate::api::workloads::WorkloadsError;
25use crate::auth::CredentialError;
26use crate::catalog_types::ProjectCommitId;
27#[cfg(feature = "tables")]
28use crate::scandal::error::ScandalError;
29#[cfg(feature = "tables")]
30use crate::scandal::types::PreparedWriteReceipt;
31
32#[derive(Debug, thiserror::Error)]
34pub enum Error {
35 #[error(transparent)]
37 Credentials(#[from] CredentialError),
38 #[error(transparent)]
40 Http(#[from] HttpError),
41 #[error("{url} returned {status}: {body}")]
43 Rejected {
44 url: Url,
45 status: StatusCode,
46 body: String,
47 },
48 #[error(transparent)]
50 Applications(Box<CallError<AppsError>>),
51 #[error(transparent)]
53 Organization(Box<CallError<OrganizationError>>),
54 #[error(transparent)]
56 Project(Box<CallError<ProjectError>>),
57 #[error(transparent)]
59 Authorization(Box<CallError<AuthorizationError>>),
60 #[error(transparent)]
62 Workloads(Box<CallError<WorkloadsError>>),
63 #[error(transparent)]
65 Location(Box<CallError<LocationsError>>),
66 #[error(transparent)]
68 Tasks(Box<CallError<TasksError>>),
69 #[error(transparent)]
71 VectorIndex(Box<CallError<VectorIndexesError>>),
72 #[error(transparent)]
74 Storage(Box<CallError<StorageError>>),
75 #[error(transparent)]
77 Enrichments(Box<CallError<EnrichmentsError>>),
78 #[error(transparent)]
80 Workers(Box<CallError<WorkersError>>),
81 #[error(transparent)]
83 Authn(Box<CallError<AuthnError>>),
84 #[error(transparent)]
86 Usage(Box<CallError<UsageError>>),
87 #[error(
89 "no Spiral credentials found; configure a bearer token, workload credentials, or a credential provider"
90 )]
91 CredentialsNotFound,
92 #[error("invalid service response: {0}")]
94 Protocol(String),
95 #[error(transparent)]
97 Arbor(Box<CallError<TablesError>>),
98 #[error(transparent)]
100 Connection(Box<CallError<ConnectionsError>>),
101 #[error("resource not found: {0}")]
103 NotFound(String),
104 #[error("the branch moved to {current} before publication")]
106 BranchMoved { current: ProjectCommitId },
107 #[error("a staged change conflicts with the branch head")]
109 Conflict(Box<PublicationConflict>),
110 #[error("publication outcome unknown for {operation_id}: {message}")]
113 OutcomeUnknown { operation_id: Uuid, message: String },
114 #[cfg(feature = "tables")]
118 #[error("table registration outcome unknown: {message}")]
119 RegistrationUnknown {
120 receipt: PreparedWriteReceipt,
121 message: String,
122 },
123 #[error("invalid SDK configuration: {0}")]
125 InvalidConfiguration(String),
126 #[error(transparent)]
128 #[cfg(feature = "tables")]
129 Scandal(ScandalError),
130 #[error("Task {task_id} failed: {message}")]
132 TaskFailed {
133 task_id: Uuid,
134 code: Option<String>,
135 message: String,
136 },
137 #[error("Task {task_id} was cancelled")]
139 TaskCancelled { task_id: Uuid },
140 #[error("timed out waiting for Task {task_id}")]
142 TaskWaitTimeout { task_id: Uuid },
143 #[error(transparent)]
145 Catalog(Box<CallError<CatalogError>>),
146 #[error("Arrow stream failed: {0}")]
148 Arrow(String),
149}
150
151impl Error {
152 pub(crate) fn invalid(error: impl Display) -> Self {
153 Self::InvalidConfiguration(error.to_string())
154 }
155
156 #[cfg(feature = "tables")]
157 pub(crate) fn arrow(error: impl Display) -> Self {
158 Self::Arrow(error.to_string())
159 }
160}
161
162#[cfg(feature = "tables")]
163impl From<ScandalError> for Error {
164 fn from(error: ScandalError) -> Self {
165 Self::Scandal(error)
166 }
167}
168
169impl From<CallError<AppsError>> for Error {
170 fn from(error: CallError<AppsError>) -> Self {
171 Self::Applications(Box::new(error))
172 }
173}
174
175impl From<reqwest_middleware::Error> for Error {
176 fn from(error: reqwest_middleware::Error) -> Self {
177 match error {
178 reqwest_middleware::Error::Reqwest(error) => Self::Http(error),
179 reqwest_middleware::Error::Middleware(error) => Self::Protocol(error.to_string()),
180 }
181 }
182}
183
184macro_rules! service_error_conversion {
185 ($variant:ident, $error:ty) => {
186 impl From<CallError<$error>> for Error {
187 fn from(error: CallError<$error>) -> Self {
188 Self::$variant(Box::new(error))
189 }
190 }
191 };
192}
193service_error_conversion!(Arbor, TablesError);
194service_error_conversion!(Catalog, CatalogError);
195service_error_conversion!(Connection, ConnectionsError);
196service_error_conversion!(Organization, OrganizationError);
197service_error_conversion!(Project, ProjectError);
198service_error_conversion!(Authorization, AuthorizationError);
199service_error_conversion!(Workloads, WorkloadsError);
200service_error_conversion!(Location, LocationsError);
201service_error_conversion!(Tasks, TasksError);
202service_error_conversion!(VectorIndex, VectorIndexesError);
203service_error_conversion!(Storage, StorageError);
204service_error_conversion!(Enrichments, EnrichmentsError);
205service_error_conversion!(Workers, WorkersError);
206service_error_conversion!(Authn, AuthnError);
207service_error_conversion!(Usage, UsageError);