1use std::any;
18use thiserror::Error;
19
20use crate::api;
21use crate::auth::{
22 authtoken::AuthTokenError, authtoken_scope::AuthTokenScopeError, v3websso::WebSsoError,
23 AuthError,
24};
25use crate::catalog::CatalogError;
26use crate::config::ConfigError;
27
28#[derive(Debug, Error)]
30#[non_exhaustive]
31pub enum RestError {
32 #[error("error setting auth header: {}", source)]
34 AuthError {
35 #[from]
37 source: AuthError,
38 },
39
40 #[error("communication with openstack: {}", source)]
42 Communication {
43 #[from]
45 source: reqwest::Error,
46 },
47
48 #[error("`http` error: {}", source)]
50 Http {
51 #[from]
53 source: http::Error,
54 },
55}
56
57#[derive(Debug, Error)]
59#[non_exhaustive]
60pub enum OpenStackError {
61 #[error("failed to parse url: {}", source)]
63 UrlParse {
64 #[from]
66 source: url::ParseError,
67 },
68
69 #[error("No authentication information available")]
71 NoAuth,
72
73 #[error("error setting auth header: {}", source)]
75 AuthError {
76 #[from]
78 source: AuthError,
79 },
80
81 #[error("communication with cloud: {}", source)]
83 Communication {
84 #[from]
86 source: reqwest::Error,
87 },
88
89 #[error("openstack HTTP error: {}", status)]
91 Http { status: reqwest::StatusCode },
92
93 #[error("no response from API")]
95 NoResponse {},
96
97 #[error("could not parse {} data from JSON: {}", typename, source)]
99 DataType {
100 #[source]
102 source: serde_json::Error,
103 typename: &'static str,
105 },
106
107 #[error("api error: {}", source)]
109 Api {
110 #[from]
112 source: api::ApiError<RestError>,
113 },
114
115 #[error("service_catalog error: {}", source)]
117 Catalog {
118 #[from]
120 source: CatalogError,
122 },
123
124 #[error("configuration error: {}", source)]
125 ConfigError {
126 #[from]
128 source: ConfigError,
129 },
130
131 #[error(
133 "`{}` endpoint version discovery error:\n\tUrl: {}\n\tMessage: {}",
134 service,
135 url,
136 msg
137 )]
138 Discovery {
139 service: String,
140 url: String,
141 msg: String,
142 },
143
144 #[error(
146 "Interactive mode is required but not available (running `echo foo | osc`?). {}",
147 msg
148 )]
149 NonInteractiveMode { msg: String },
150
151 #[error("could not parse JSON response: {}", source)]
153 Json {
154 #[from]
156 source: serde_json::Error,
157 },
158 #[error("IO error: {}\n\tPath: {}", source, path)]
160 IO {
161 source: std::io::Error,
163 path: String,
164 },
165
166 #[error("endpoint builder error: `{0}`")]
168 EndpointBuild(String),
169}
170
171impl OpenStackError {
172 pub fn http(status: reqwest::StatusCode) -> Self {
173 OpenStackError::Http { status }
174 }
175
176 pub fn no_response() -> Self {
177 OpenStackError::NoResponse {}
178 }
179
180 pub fn data_type<T>(source: serde_json::Error) -> Self {
181 OpenStackError::DataType {
182 source,
183 typename: any::type_name::<T>(),
184 }
185 }
186
187 pub fn catalog(source: CatalogError) -> Self {
188 OpenStackError::Catalog { source }
189 }
190}
191
192impl From<AuthTokenError> for OpenStackError {
194 fn from(source: AuthTokenError) -> Self {
195 Self::AuthError {
196 source: AuthError::AuthToken { source },
197 }
198 }
199}
200
201impl From<AuthTokenScopeError> for OpenStackError {
202 fn from(source: AuthTokenScopeError) -> Self {
203 Self::AuthError {
204 source: source.into(),
205 }
206 }
207}
208
209impl From<WebSsoError> for OpenStackError {
210 fn from(source: WebSsoError) -> Self {
211 Self::AuthError {
212 source: source.into(),
213 }
214 }
215}
216
217pub type OpenStackResult<T> = Result<T, OpenStackError>;