1use std::fmt::Debug;
7
8use serde::{Deserialize, Serialize, de::DeserializeOwned};
9
10use crate::oauth10a::{
11 Client as OAuthClient, ClientError, Request, RestClient,
12 reqwest::{self, Method},
13};
14
15pub mod v2;
16pub mod v4;
17
18pub use oauth10a::client as oauth10a;
22
23pub const PUBLIC_ENDPOINT: &str = "https://api.clever-cloud.com";
27pub const PUBLIC_API_BRIDGE_ENDPOINT: &str = "https://api-bridge.clever-cloud.com";
28
29pub const DEFAULT_CONSUMER_KEY: &str = "T5nFjKeHH4AIlEveuGhB5S3xg8T19e";
37pub fn default_consumer_key() -> String {
38 DEFAULT_CONSUMER_KEY.to_string()
39}
40
41pub const DEFAULT_CONSUMER_SECRET: &str = "MgVMqTr6fWlf2M0tkC2MXOnhfqBWDT";
42pub fn default_consumer_secret() -> String {
43 DEFAULT_CONSUMER_SECRET.to_string()
44}
45
46#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
50#[serde(untagged)]
51pub enum Credentials {
52 OAuth1 {
53 #[serde(rename = "token")]
54 token: String,
55 #[serde(rename = "secret")]
56 secret: String,
57 #[serde(rename = "consumer-key", default = "default_consumer_key")]
58 consumer_key: String,
59 #[serde(rename = "consumer-secret", default = "default_consumer_secret")]
60 consumer_secret: String,
61 },
62 Basic {
63 #[serde(rename = "username")]
64 username: String,
65 #[serde(rename = "password")]
66 password: String,
67 },
68 Bearer {
69 #[serde(rename = "token")]
70 token: String,
71 },
72}
73
74impl Default for Credentials {
75 #[tracing::instrument(skip_all)]
76 fn default() -> Self {
77 Self::OAuth1 {
78 token: String::new(),
79 secret: String::new(),
80 consumer_key: DEFAULT_CONSUMER_KEY.to_string(),
81 consumer_secret: DEFAULT_CONSUMER_SECRET.to_string(),
82 }
83 }
84}
85
86impl From<oauth10a::Credentials> for Credentials {
87 #[tracing::instrument(skip_all)]
88 fn from(credentials: oauth10a::Credentials) -> Self {
89 match credentials {
90 oauth10a::Credentials::Bearer { token } => Self::Bearer { token },
91 oauth10a::Credentials::Basic { username, password } => {
92 Self::Basic { username, password }
93 }
94 oauth10a::Credentials::OAuth1 {
95 token,
96 secret,
97 consumer_key,
98 consumer_secret,
99 } => Self::OAuth1 {
100 token,
101 secret,
102 consumer_key,
103 consumer_secret,
104 },
105 }
106 }
107}
108
109#[allow(clippy::from_over_into)]
110impl Into<oauth10a::Credentials> for Credentials {
111 #[tracing::instrument(skip_all)]
112 fn into(self) -> oauth10a::Credentials {
113 match self {
114 Self::Bearer { token } => oauth10a::Credentials::Bearer { token },
115 Self::Basic { username, password } => {
116 oauth10a::Credentials::Basic { username, password }
117 }
118 Self::OAuth1 {
119 token,
120 secret,
121 consumer_key,
122 consumer_secret,
123 } => oauth10a::Credentials::OAuth1 {
124 token,
125 secret,
126 consumer_key,
127 consumer_secret,
128 },
129 }
130 }
131}
132
133impl Credentials {
134 #[tracing::instrument(skip_all)]
135 pub fn bearer(token: String) -> Self {
136 Self::Bearer { token }
137 }
138
139 #[tracing::instrument(skip_all)]
140 pub fn basic(username: String, password: String) -> Self {
141 Self::Basic { username, password }
142 }
143
144 #[tracing::instrument(skip_all)]
145 pub fn oauth1(
146 token: String,
147 secret: String,
148 consumer_key: String,
149 consumer_secret: String,
150 ) -> Self {
151 Self::OAuth1 {
152 token,
153 secret,
154 consumer_key,
155 consumer_secret,
156 }
157 }
158}
159
160#[derive(Clone, Debug, Default)]
164pub struct Builder {
165 endpoint: Option<String>,
166 credentials: Option<Credentials>,
167}
168
169impl Builder {
170 #[cfg_attr(feature = "tracing", tracing::instrument)]
171 pub fn with_endpoint(mut self, endpoint: String) -> Self {
172 self.endpoint = Some(endpoint);
173 self
174 }
175
176 #[cfg_attr(feature = "tracing", tracing::instrument)]
177 pub fn with_credentials(mut self, credentials: Credentials) -> Self {
178 self.credentials = Some(credentials);
179 self
180 }
181
182 #[cfg_attr(feature = "tracing", tracing::instrument)]
183 pub fn build(self, client: reqwest::Client) -> Client {
184 let endpoint = match self.endpoint {
185 Some(endpoint) => endpoint,
186 None => {
187 if matches!(self.credentials, Some(Credentials::Bearer { .. })) {
188 PUBLIC_API_BRIDGE_ENDPOINT.to_string()
189 } else {
190 PUBLIC_ENDPOINT.to_string()
191 }
192 }
193 };
194
195 Client {
196 inner: OAuthClient::new(client, self.credentials.map(Into::into)),
197 endpoint,
198 }
199 }
200}
201
202#[derive(Clone, Debug)]
206pub struct Client {
207 inner: OAuthClient,
208 endpoint: String,
209}
210
211impl Request for Client {
212 type Error = ClientError;
213
214 #[cfg_attr(feature = "tracing", tracing::instrument)]
215 fn request<T, U>(
216 &self,
217 method: &Method,
218 endpoint: &str,
219 payload: &T,
220 ) -> impl Future<Output = Result<U, Self::Error>>
221 where
222 T: Serialize + Debug + Send + Sync,
223 U: DeserializeOwned + Debug + Send + Sync,
224 {
225 self.inner.request(method, endpoint, payload)
226 }
227
228 #[cfg_attr(feature = "tracing", tracing::instrument)]
229 fn execute(
230 &self,
231 request: reqwest::Request,
232 ) -> impl Future<Output = Result<reqwest::Response, Self::Error>> {
233 self.inner.execute(request)
234 }
235}
236
237impl RestClient for Client {
238 type Error = ClientError;
239
240 #[cfg_attr(feature = "tracing", tracing::instrument)]
241 fn get<T>(&self, endpoint: &str) -> impl Future<Output = Result<T, Self::Error>>
242 where
243 T: DeserializeOwned + Debug + Send + Sync,
244 {
245 self.inner.get(endpoint)
246 }
247
248 #[cfg_attr(feature = "tracing", tracing::instrument)]
249 fn post<T, U>(
250 &self,
251 endpoint: &str,
252 payload: &T,
253 ) -> impl Future<Output = Result<U, Self::Error>>
254 where
255 T: Serialize + Debug + Send + Sync,
256 U: DeserializeOwned + Debug + Send + Sync,
257 {
258 self.inner.post(endpoint, payload)
259 }
260
261 #[cfg_attr(feature = "tracing", tracing::instrument)]
262 fn put<T, U>(&self, endpoint: &str, payload: &T) -> impl Future<Output = Result<U, Self::Error>>
263 where
264 T: Serialize + Debug + Send + Sync,
265 U: DeserializeOwned + Debug + Send + Sync,
266 {
267 self.inner.put(endpoint, payload)
268 }
269
270 #[cfg_attr(feature = "tracing", tracing::instrument)]
271 fn patch<T, U>(
272 &self,
273 endpoint: &str,
274 payload: &T,
275 ) -> impl Future<Output = Result<U, Self::Error>>
276 where
277 T: Serialize + Debug + Send + Sync,
278 U: DeserializeOwned + Debug + Send + Sync,
279 {
280 self.inner.patch(endpoint, payload)
281 }
282
283 #[cfg_attr(feature = "tracing", tracing::instrument)]
284 fn delete(&self, endpoint: &str) -> impl Future<Output = Result<(), Self::Error>> {
285 self.inner.delete(endpoint)
286 }
287}
288
289impl From<reqwest::Client> for Client {
290 #[cfg_attr(feature = "tracing", tracing::instrument)]
291 fn from(client: reqwest::Client) -> Self {
292 Self::builder().build(client)
293 }
294}
295
296impl From<Credentials> for Client {
297 #[cfg_attr(feature = "tracing", tracing::instrument)]
298 fn from(credentials: Credentials) -> Self {
299 match &credentials {
300 Credentials::Bearer { .. } => Self::builder()
301 .with_endpoint(PUBLIC_API_BRIDGE_ENDPOINT.to_string())
302 .with_credentials(credentials)
303 .build(reqwest::Client::new()),
304 _ => Self::builder()
305 .with_credentials(credentials)
306 .build(reqwest::Client::new()),
307 }
308 }
309}
310
311impl Default for Client {
312 #[cfg_attr(feature = "tracing", tracing::instrument)]
313 fn default() -> Self {
314 Self::builder().build(reqwest::Client::new())
315 }
316}
317
318impl Client {
319 #[cfg_attr(feature = "tracing", tracing::instrument)]
320 pub fn new(
321 client: reqwest::Client,
322 endpoint: String,
323 credentials: Option<Credentials>,
324 ) -> Self {
325 let mut builder = Self::builder().with_endpoint(endpoint);
326
327 if let Some(credentials) = credentials {
328 builder = builder.with_credentials(credentials);
329 }
330
331 builder.build(client)
332 }
333
334 #[cfg_attr(feature = "tracing", tracing::instrument)]
335 pub fn builder() -> Builder {
336 Builder::default()
337 }
338
339 #[cfg_attr(feature = "tracing", tracing::instrument)]
340 pub fn set_endpoint(&mut self, endpoint: String) {
341 self.endpoint = endpoint;
342 }
343
344 #[cfg_attr(feature = "tracing", tracing::instrument)]
345 pub fn set_credentials(&mut self, credentials: Option<Credentials>) {
346 self.inner.set_credentials(credentials.map(Into::into));
347 }
348
349 #[cfg_attr(feature = "tracing", tracing::instrument)]
350 pub fn inner(&self) -> &reqwest::Client {
351 self.inner.inner()
352 }
353}