use std::fmt::Display;
use reqwest::{Error as HttpError, StatusCode, Url};
use spiral_contract::call::CallError;
use uuid::Uuid;
use crate::api::apps::AppsError;
use crate::api::authn::AuthnError;
use crate::api::authorization::AuthorizationError;
use crate::api::catalog::{CatalogError, PublicationConflict};
use crate::api::connections::ConnectionsError;
use crate::api::enrichments::EnrichmentsError;
use crate::api::locations::LocationsError;
use crate::api::organization::OrganizationError;
use crate::api::project::ProjectError;
use crate::api::storage::StorageError;
use crate::api::tables::TablesError;
use crate::api::tasks::TasksError;
use crate::api::usage::UsageError;
use crate::api::vector_indexes::VectorIndexesError;
use crate::api::workers::WorkersError;
use crate::api::workloads::WorkloadsError;
use crate::auth::CredentialError;
use crate::catalog_types::ProjectCommitId;
#[cfg(feature = "tables")]
use crate::scandal::error::ScandalError;
#[cfg(feature = "tables")]
use crate::scandal::types::PreparedWriteReceipt;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Credentials(#[from] CredentialError),
#[error(transparent)]
Http(#[from] HttpError),
#[error("{url} returned {status}: {body}")]
Rejected {
url: Url,
status: StatusCode,
body: String,
},
#[error(transparent)]
Applications(Box<CallError<AppsError>>),
#[error(transparent)]
Organization(Box<CallError<OrganizationError>>),
#[error(transparent)]
Project(Box<CallError<ProjectError>>),
#[error(transparent)]
Authorization(Box<CallError<AuthorizationError>>),
#[error(transparent)]
Workloads(Box<CallError<WorkloadsError>>),
#[error(transparent)]
Location(Box<CallError<LocationsError>>),
#[error(transparent)]
Tasks(Box<CallError<TasksError>>),
#[error(transparent)]
VectorIndex(Box<CallError<VectorIndexesError>>),
#[error(transparent)]
Storage(Box<CallError<StorageError>>),
#[error(transparent)]
Enrichments(Box<CallError<EnrichmentsError>>),
#[error(transparent)]
Workers(Box<CallError<WorkersError>>),
#[error(transparent)]
Authn(Box<CallError<AuthnError>>),
#[error(transparent)]
Usage(Box<CallError<UsageError>>),
#[error(
"no Spiral credentials found; configure a bearer token, workload credentials, or a credential provider"
)]
CredentialsNotFound,
#[error("invalid service response: {0}")]
Protocol(String),
#[error(transparent)]
Arbor(Box<CallError<TablesError>>),
#[error(transparent)]
Connection(Box<CallError<ConnectionsError>>),
#[error("resource not found: {0}")]
NotFound(String),
#[error("the branch moved to {current} before publication")]
BranchMoved { current: ProjectCommitId },
#[error("a staged change conflicts with the branch head")]
Conflict(Box<PublicationConflict>),
#[error("publication outcome unknown for {operation_id}: {message}")]
OutcomeUnknown { operation_id: Uuid, message: String },
#[cfg(feature = "tables")]
#[error("table registration outcome unknown: {message}")]
RegistrationUnknown {
receipt: PreparedWriteReceipt,
message: String,
},
#[error("invalid SDK configuration: {0}")]
InvalidConfiguration(String),
#[error(transparent)]
#[cfg(feature = "tables")]
Scandal(ScandalError),
#[error("Task {task_id} failed: {message}")]
TaskFailed {
task_id: Uuid,
code: Option<String>,
message: String,
},
#[error("Task {task_id} was cancelled")]
TaskCancelled { task_id: Uuid },
#[error("timed out waiting for Task {task_id}")]
TaskWaitTimeout { task_id: Uuid },
#[error(transparent)]
Catalog(Box<CallError<CatalogError>>),
#[error("Arrow stream failed: {0}")]
Arrow(String),
}
impl Error {
pub(crate) fn invalid(error: impl Display) -> Self {
Self::InvalidConfiguration(error.to_string())
}
#[cfg(feature = "tables")]
pub(crate) fn arrow(error: impl Display) -> Self {
Self::Arrow(error.to_string())
}
}
#[cfg(feature = "tables")]
impl From<ScandalError> for Error {
fn from(error: ScandalError) -> Self {
Self::Scandal(error)
}
}
impl From<CallError<AppsError>> for Error {
fn from(error: CallError<AppsError>) -> Self {
Self::Applications(Box::new(error))
}
}
impl From<reqwest_middleware::Error> for Error {
fn from(error: reqwest_middleware::Error) -> Self {
match error {
reqwest_middleware::Error::Reqwest(error) => Self::Http(error),
reqwest_middleware::Error::Middleware(error) => Self::Protocol(error.to_string()),
}
}
}
macro_rules! service_error_conversion {
($variant:ident, $error:ty) => {
impl From<CallError<$error>> for Error {
fn from(error: CallError<$error>) -> Self {
Self::$variant(Box::new(error))
}
}
};
}
service_error_conversion!(Arbor, TablesError);
service_error_conversion!(Catalog, CatalogError);
service_error_conversion!(Connection, ConnectionsError);
service_error_conversion!(Organization, OrganizationError);
service_error_conversion!(Project, ProjectError);
service_error_conversion!(Authorization, AuthorizationError);
service_error_conversion!(Workloads, WorkloadsError);
service_error_conversion!(Location, LocationsError);
service_error_conversion!(Tasks, TasksError);
service_error_conversion!(VectorIndex, VectorIndexesError);
service_error_conversion!(Storage, StorageError);
service_error_conversion!(Enrichments, EnrichmentsError);
service_error_conversion!(Workers, WorkersError);
service_error_conversion!(Authn, AuthnError);
service_error_conversion!(Usage, UsageError);