use std::any;
use thiserror::Error;
use crate::api;
#[cfg(feature = "passkey")]
use crate::auth::v4passkey::PasskeyError;
use crate::auth::{
authtoken::AuthTokenError, authtoken_scope::AuthTokenScopeError,
v3oidcaccesstoken::OidcAccessTokenError, v3websso::WebSsoError, AuthError,
};
#[cfg(feature = "keystone_ng")]
use crate::auth::{v4federation::FederationError, v4jwt::JwtError};
use crate::catalog::CatalogError;
use crate::config::ConfigError;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum RestError {
#[error("error setting auth header: {}", source)]
AuthError {
#[from]
source: AuthError,
},
#[error("communication with openstack: {}", source)]
Communication {
#[from]
source: reqwest::Error,
},
#[error("`http` error: {}", source)]
Http {
#[from]
source: http::Error,
},
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum OpenStackError {
#[error("failed to parse url: {}", source)]
UrlParse {
#[from]
source: url::ParseError,
},
#[error("No authentication information available")]
NoAuth,
#[error("error setting auth header: {}", source)]
AuthError {
#[from]
source: AuthError,
},
#[error("communication with cloud: {}", source)]
Communication {
#[from]
source: reqwest::Error,
},
#[error("openstack HTTP error: {}", status)]
Http { status: reqwest::StatusCode },
#[error("no response from API")]
NoResponse {},
#[error("could not parse {} data from JSON: {}", typename, source)]
DataType {
#[source]
source: serde_json::Error,
typename: &'static str,
},
#[error("api error: {}", source)]
Api {
#[from]
source: api::ApiError<RestError>,
},
#[error("service_catalog error: {}", source)]
Catalog {
#[from]
source: CatalogError,
},
#[error("configuration error: {}", source)]
ConfigError {
#[from]
source: ConfigError,
},
#[error("error reading the user input: {}", source)]
Dialoguer {
#[from]
source: dialoguer::Error,
},
#[error(
"`{}` endpoint version discovery error:\n\tUrl: {}\n\tMessage: {}",
service,
url,
msg
)]
Discovery {
service: String,
url: String,
msg: String,
},
#[error(
"Interactive mode is required but not available (running `echo foo | osc`?). {}",
msg
)]
NonInteractiveMode { msg: String },
#[error("could not parse JSON response: {}", source)]
Json {
#[from]
source: serde_json::Error,
},
#[error("general IO error: {}", source)]
IO {
#[from]
source: std::io::Error,
},
#[error("IO error: {}\n\tPath: {}", source, path)]
IOWithPath {
source: std::io::Error,
path: String,
},
#[error("invalid url: {}", source)]
InvalidUri {
#[from]
source: http::uri::InvalidUri,
},
#[error("endpoint builder error: `{0}`")]
EndpointBuild(String),
}
impl OpenStackError {
pub fn http(status: reqwest::StatusCode) -> Self {
OpenStackError::Http { status }
}
pub fn no_response() -> Self {
OpenStackError::NoResponse {}
}
pub fn data_type<T>(source: serde_json::Error) -> Self {
OpenStackError::DataType {
source,
typename: any::type_name::<T>(),
}
}
pub fn catalog(source: CatalogError) -> Self {
OpenStackError::Catalog { source }
}
}
impl From<AuthTokenError> for OpenStackError {
fn from(source: AuthTokenError) -> Self {
Self::AuthError {
source: AuthError::AuthToken { source },
}
}
}
impl From<AuthTokenScopeError> for OpenStackError {
fn from(source: AuthTokenScopeError) -> Self {
Self::AuthError {
source: source.into(),
}
}
}
impl From<OidcAccessTokenError> for OpenStackError {
fn from(source: OidcAccessTokenError) -> Self {
Self::AuthError {
source: source.into(),
}
}
}
impl From<WebSsoError> for OpenStackError {
fn from(source: WebSsoError) -> Self {
Self::AuthError {
source: source.into(),
}
}
}
#[cfg(feature = "keystone_ng")]
impl From<FederationError> for OpenStackError {
fn from(source: FederationError) -> Self {
Self::AuthError {
source: source.into(),
}
}
}
#[cfg(feature = "keystone_ng")]
impl From<JwtError> for OpenStackError {
fn from(source: JwtError) -> Self {
Self::AuthError {
source: source.into(),
}
}
}
#[cfg(feature = "passkey")]
impl From<PasskeyError> for OpenStackError {
fn from(source: PasskeyError) -> Self {
Self::AuthError {
source: source.into(),
}
}
}
pub type OpenStackResult<T> = Result<T, OpenStackError>;