openstack_sdk/
error.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12//
13// SPDX-License-Identifier: Apache-2.0
14
15//! Errors module
16
17use 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/// Rest errors that may happen during API communication
29#[derive(Debug, Error)]
30#[non_exhaustive]
31pub enum RestError {
32    /// Auth error
33    #[error("error setting auth header: {}", source)]
34    AuthError {
35        /// The source of the error.
36        #[from]
37        source: AuthError,
38    },
39
40    /// API communication error
41    #[error("communication with openstack: {}", source)]
42    Communication {
43        /// The source of the error.
44        #[from]
45        source: reqwest::Error,
46    },
47
48    /// HTTP error
49    #[error("`http` error: {}", source)]
50    Http {
51        /// The source of the error.
52        #[from]
53        source: http::Error,
54    },
55}
56
57/// OpenStack Client error
58#[derive(Debug, Error)]
59#[non_exhaustive]
60pub enum OpenStackError {
61    /// URL parse error
62    #[error("failed to parse url: {}", source)]
63    UrlParse {
64        /// The source of the error.
65        #[from]
66        source: url::ParseError,
67    },
68
69    /// Authentication error
70    #[error("No authentication information available")]
71    NoAuth,
72
73    /// Authentication error
74    #[error("error setting auth header: {}", source)]
75    AuthError {
76        /// The source of the error.
77        #[from]
78        source: AuthError,
79    },
80
81    /// API Communication error
82    #[error("communication with cloud: {}", source)]
83    Communication {
84        /// The source of the error.
85        #[from]
86        source: reqwest::Error,
87    },
88
89    /// HTTP error
90    #[error("openstack HTTP error: {}", status)]
91    Http { status: reqwest::StatusCode },
92
93    /// No response
94    #[error("no response from API")]
95    NoResponse {},
96
97    /// Json deserialization error
98    #[error("could not parse {} data from JSON: {}", typename, source)]
99    DataType {
100        /// The source of the error.
101        #[source]
102        source: serde_json::Error,
103        /// type name that could not be parsed
104        typename: &'static str,
105    },
106
107    /// API error
108    #[error("api error: {}", source)]
109    Api {
110        /// The source of the error.
111        #[from]
112        source: api::ApiError<RestError>,
113    },
114
115    /// Service catalog error
116    #[error("service_catalog error: {}", source)]
117    Catalog {
118        /// The source of the error.
119        #[from]
120        /// error source
121        source: CatalogError,
122    },
123
124    #[error("configuration error: {}", source)]
125    ConfigError {
126        /// The source of the error.
127        #[from]
128        source: ConfigError,
129    },
130
131    /// Service version discovery error
132    #[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    /// Interactive mode required
145    #[error(
146        "Interactive mode is required but not available (running `echo foo | osc`?). {}",
147        msg
148    )]
149    NonInteractiveMode { msg: String },
150
151    /// JSON deserialization from OpenStack failed.
152    #[error("could not parse JSON response: {}", source)]
153    Json {
154        /// The source of the error.
155        #[from]
156        source: serde_json::Error,
157    },
158    /// IO error.
159    #[error("IO error: {}\n\tPath: {}", source, path)]
160    IO {
161        /// The source of the error.
162        source: std::io::Error,
163        path: String,
164    },
165
166    /// Endpoint builder error
167    #[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
192// Explicitly implement From to easier propagate nested errors
193impl 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>;