Skip to main content

spiral/
error.rs

1//! Errors produced by SDK configuration, control-plane management and table operations.
2
3use 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/// A Rust SDK operation failed.
33#[derive(Debug, thiserror::Error)]
34pub enum Error {
35    /// A credential source could not supply a usable credential.
36    #[error(transparent)]
37    Credentials(#[from] CredentialError),
38    /// An HTTP request or response failed.
39    #[error(transparent)]
40    Http(#[from] HttpError),
41    /// A control-plane request was rejected.
42    #[error("{url} returned {status}: {body}")]
43    Rejected {
44        url: Url,
45        status: StatusCode,
46        body: String,
47    },
48    /// An application or installation operation failed, retaining its typed service error.
49    #[error(transparent)]
50    Applications(Box<CallError<AppsError>>),
51    /// A typed organization service call failed.
52    #[error(transparent)]
53    Organization(Box<CallError<OrganizationError>>),
54    /// A typed project service call failed.
55    #[error(transparent)]
56    Project(Box<CallError<ProjectError>>),
57    /// A typed authorization service call failed.
58    #[error(transparent)]
59    Authorization(Box<CallError<AuthorizationError>>),
60    /// A typed workloads service call failed.
61    #[error(transparent)]
62    Workloads(Box<CallError<WorkloadsError>>),
63    /// A typed locations service call failed.
64    #[error(transparent)]
65    Location(Box<CallError<LocationsError>>),
66    /// A typed tasks service call failed.
67    #[error(transparent)]
68    Tasks(Box<CallError<TasksError>>),
69    /// A typed vector indexes service call failed.
70    #[error(transparent)]
71    VectorIndex(Box<CallError<VectorIndexesError>>),
72    /// A typed storage service call failed.
73    #[error(transparent)]
74    Storage(Box<CallError<StorageError>>),
75    /// A typed enrichments service call failed.
76    #[error(transparent)]
77    Enrichments(Box<CallError<EnrichmentsError>>),
78    /// A typed workers service call failed.
79    #[error(transparent)]
80    Workers(Box<CallError<WorkersError>>),
81    /// A typed authn service call failed.
82    #[error(transparent)]
83    Authn(Box<CallError<AuthnError>>),
84    /// A typed usage service call failed.
85    #[error(transparent)]
86    Usage(Box<CallError<UsageError>>),
87    /// No credential source supplied a usable credential.
88    #[error(
89        "no Spiral credentials found; configure a bearer token, workload credentials, or a credential provider"
90    )]
91    CredentialsNotFound,
92    /// A remote response violated its protocol.
93    #[error("invalid service response: {0}")]
94    Protocol(String),
95    /// A typed Arbor resource-service operation failed.
96    #[error(transparent)]
97    Arbor(Box<CallError<TablesError>>),
98    /// A typed Connection service operation failed.
99    #[error(transparent)]
100    Connection(Box<CallError<ConnectionsError>>),
101    /// A resource path has no binding at the selected commit.
102    #[error("resource not found: {0}")]
103    NotFound(String),
104    /// A commit was not published because its branch moved past the pinned head.
105    #[error("the branch moved to {current} before publication")]
106    BranchMoved { current: ProjectCommitId },
107    /// A commit was not published because a staged change conflicts with the branch head.
108    #[error("a staged change conflicts with the branch head")]
109    Conflict(Box<PublicationConflict>),
110    /// Publication may have committed. Match [`PublishError::Unknown`](crate::PublishError)
111    /// before converting, to recover or publish the same frozen commit.
112    #[error("publication outcome unknown for {operation_id}: {message}")]
113    OutcomeUnknown { operation_id: Uuid, message: String },
114    /// Resource registration may have succeeded. Register the receipt again with
115    /// [`Table::recover_write`](crate::Table::recover_write) for a table's next version, or
116    /// [`Client::register_prepared`](crate::Client::register_prepared) for a new table.
117    #[cfg(feature = "tables")]
118    #[error("table registration outcome unknown: {message}")]
119    RegistrationUnknown {
120        receipt: PreparedWriteReceipt,
121        message: String,
122    },
123    /// Client configuration is invalid.
124    #[error("invalid SDK configuration: {0}")]
125    InvalidConfiguration(String),
126    /// A scandal operation failed.
127    #[error(transparent)]
128    #[cfg(feature = "tables")]
129    Scandal(ScandalError),
130    /// A Task finished without succeeding.
131    #[error("Task {task_id} failed: {message}")]
132    TaskFailed {
133        task_id: Uuid,
134        code: Option<String>,
135        message: String,
136    },
137    /// A Task was cancelled before it succeeded.
138    #[error("Task {task_id} was cancelled")]
139    TaskCancelled { task_id: Uuid },
140    /// The client stopped waiting; the Task may still be running.
141    #[error("timed out waiting for Task {task_id}")]
142    TaskWaitTimeout { task_id: Uuid },
143    /// A project catalog operation failed.
144    #[error(transparent)]
145    Catalog(Box<CallError<CatalogError>>),
146    /// Arrow data could not be encoded or decoded.
147    #[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);