1use http_api_client::{
2 http::{HeaderMap, Method, StatusCode},
3 Client,
4};
5use serde::{de::DeserializeOwned, Serialize};
6use url::Url;
7
8use crate::endpoints::v4::{
9 parse_response, render_request, AccessToken, ErrorResponseBody, ParseResponseError,
10 RenderRequestError,
11};
12
13pub struct V4Client<C>
15where
16 C: Client,
17{
18 inner: C,
19 pub access_token: Option<AccessToken>,
20 pub base_url: Option<Url>,
21}
22
23impl<C> Clone for V4Client<C>
24where
25 C: Client + Clone,
26{
27 fn clone(&self) -> Self {
28 Self {
29 inner: self.inner.clone(),
30 access_token: self.access_token.clone(),
31 base_url: self.base_url.clone(),
32 }
33 }
34}
35
36impl<C> V4Client<C>
37where
38 C: Client,
39{
40 pub fn new(inner: C, access_token: Option<AccessToken>, base_url: Option<Url>) -> Self {
41 Self {
42 inner,
43 access_token,
44 base_url,
45 }
46 }
47
48 pub async fn respond<ReqQuery, ReqBody, RespBody>(
49 &self,
50 method: Method,
51 path: &str,
52 query: Option<&ReqQuery>,
53 body: Option<&ReqBody>,
54 ) -> Result<(StatusCode, RespBody, HeaderMap), V4ClientRespondError<C>>
55 where
56 ReqQuery: Serialize,
57 ReqBody: Serialize,
58 RespBody: DeserializeOwned,
59 {
60 let req = render_request::<ReqQuery, ReqBody>(
61 method,
62 path,
63 query,
64 body,
65 self.access_token.as_ref(),
66 self.base_url.as_ref(),
67 )
68 .map_err(V4ClientRespondError::RenderRequestError)?;
69
70 let resp = self
71 .inner
72 .respond(req)
73 .await
74 .map_err(V4ClientRespondError::RespondError)?;
75
76 let (resp_status, resp_body, resp_headers) =
77 parse_response::<RespBody>(resp).map_err(V4ClientRespondError::ParseResponseError)?;
78
79 match resp_body {
80 Ok(body) => Ok((resp_status, body, resp_headers)),
81 Err(body) => Err(V4ClientRespondError::ResponseStatusCodeNoSuccess(
82 resp_status,
83 body,
84 resp_headers,
85 )),
86 }
87 }
88}
89
90#[derive(Debug)]
92pub enum V4ClientRespondError<C>
93where
94 C: Client,
95{
96 RenderRequestError(RenderRequestError),
97 RespondError(C::RespondError),
98 ParseResponseError(ParseResponseError),
99 ResponseStatusCodeNoSuccess(StatusCode, ErrorResponseBody, HeaderMap),
100}
101impl<C> core::fmt::Display for V4ClientRespondError<C>
102where
103 C: Client + core::fmt::Debug,
104{
105 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106 write!(f, "{self:?}")
107 }
108}
109impl<C> std::error::Error for V4ClientRespondError<C> where C: Client + core::fmt::Debug {}
110
111mod ext {
112 use super::*;
113
114 use crate::{
115 endpoints::v4::{
116 linode_instances::{
117 config_create, disk_create, disk_view, ip_addresses_info, linode_boot,
118 linode_create, linode_reboot, linode_view, linodes_list,
119 },
120 linode_types::types_list,
121 EmptyMapResponseBody, XListRequestQuery,
122 },
123 objects::v4::linode_instances::{ConfigId, DiskId, LinodeId},
124 };
125
126 impl<C> V4Client<C>
127 where
128 C: Client,
129 {
130 pub async fn linode_instances_linodes_list(
134 &self,
135 page: impl Into<Option<usize>>,
136 page_size: impl Into<Option<usize>>,
137 ) -> Result<linodes_list::ResponseBody, V4ClientRespondError<C>> {
138 let (_resp_status, resp_body, _resp_headers) = self
139 .respond::<_, (), _>(
140 Method::GET,
141 "/linode/instances",
142 Some(&XListRequestQuery::new(page, page_size)),
143 None,
144 )
145 .await?;
146 Ok(resp_body)
147 }
148
149 pub async fn linode_instances_linode_create<F>(
150 &self,
151 region: &str,
152 r#type: &str,
153 mut f: F,
154 ) -> Result<linode_create::ResponseBody, V4ClientRespondError<C>>
155 where
156 F: FnMut(&mut linode_create::RequestBody),
157 {
158 let mut req_body = linode_create::RequestBody {
159 region: region.into(),
160 r#type: r#type.into(),
161 ..Default::default()
162 };
163 f(&mut req_body);
164
165 let (_resp_status, resp_body, _resp_headers) = self
166 .respond::<(), _, _>(Method::POST, "/linode/instances", None, Some(&req_body))
167 .await?;
168 Ok(resp_body)
169 }
170
171 pub async fn linode_instances_linode_delete(
172 &self,
173 linode_id: LinodeId,
174 ) -> Result<(), V4ClientRespondError<C>> {
175 let (_resp_status, _resp_body, _resp_headers) = self
176 .respond::<(), (), EmptyMapResponseBody>(
177 Method::DELETE,
178 format!("/linode/instances/{}", linode_id).as_str(),
179 None,
180 None,
181 )
182 .await?;
183 Ok(())
184 }
185
186 pub async fn linode_instances_linode_view(
187 &self,
188 linode_id: LinodeId,
189 ) -> Result<linode_view::ResponseBody, V4ClientRespondError<C>> {
190 let (_resp_status, resp_body, _resp_headers) = self
191 .respond::<(), (), _>(
192 Method::GET,
193 format!("/linode/instances/{}", linode_id).as_str(),
194 None,
195 None,
196 )
197 .await?;
198 Ok(resp_body)
199 }
200
201 pub async fn linode_instances_linode_boot(
202 &self,
203 linode_id: LinodeId,
204 config_id: ConfigId,
205 ) -> Result<(), V4ClientRespondError<C>> {
206 let req_body = linode_boot::RequestBody { config_id };
207 let (_resp_status, _resp_body, _resp_headers) = self
208 .respond::<(), _, EmptyMapResponseBody>(
209 Method::POST,
210 format!("/linode/instances/{}/boot", linode_id).as_str(),
211 None,
212 Some(&req_body),
213 )
214 .await?;
215 Ok(())
216 }
217
218 pub async fn linode_instances_linode_reboot(
219 &self,
220 linode_id: LinodeId,
221 config_id: Option<ConfigId>,
222 ) -> Result<(), V4ClientRespondError<C>> {
223 if let Some(config_id) = config_id {
224 let req_body = linode_reboot::RequestBody {
225 config_id: Some(config_id),
226 };
227 let (_resp_status, _resp_body, _resp_headers) = self
228 .respond::<(), _, EmptyMapResponseBody>(
229 Method::POST,
230 format!("/linode/instances/{}/reboot", linode_id).as_str(),
231 None,
232 Some(&req_body),
233 )
234 .await?;
235 } else {
236 let (_resp_status, _resp_body, _resp_headers) = self
237 .respond::<(), (), EmptyMapResponseBody>(
238 Method::POST,
239 format!("/linode/instances/{}/reboot", linode_id).as_str(),
240 None,
241 None,
242 )
243 .await?;
244 }
245
246 Ok(())
247 }
248
249 pub async fn linode_instances_ip_addresses_info(
250 &self,
251 linode_id: LinodeId,
252 ) -> Result<ip_addresses_info::ResponseBody, V4ClientRespondError<C>> {
253 let (_resp_status, resp_body, _resp_headers) = self
254 .respond::<(), (), _>(
255 Method::GET,
256 format!("/linode/instances/{}/ips", linode_id).as_str(),
257 None,
258 None,
259 )
260 .await?;
261 Ok(resp_body)
262 }
263
264 pub async fn linode_instances_config_create<F>(
265 &self,
266 linode_id: LinodeId,
267 label: &str,
268 mut f: F,
269 ) -> Result<config_create::ResponseBody, V4ClientRespondError<C>>
270 where
271 F: FnMut(&mut config_create::RequestBody),
272 {
273 let mut req_body = config_create::RequestBody {
274 label: label.into(),
275 ..Default::default()
276 };
277 f(&mut req_body);
278
279 let (_resp_status, resp_body, _resp_headers) = self
280 .respond::<(), _, _>(
281 Method::POST,
282 format!("/linode/instances/{}/configs", linode_id).as_str(),
283 None,
284 Some(&req_body),
285 )
286 .await?;
287 Ok(resp_body)
288 }
289
290 pub async fn linode_instances_disk_create_with_image<F>(
291 &self,
292 linode_id: LinodeId,
293 size: usize,
294 image: &str,
295 root_pass: &str,
296 mut f: F,
297 ) -> Result<disk_create::ResponseBody, V4ClientRespondError<C>>
298 where
299 F: FnMut(&mut disk_create::RequestBodyWithImage),
300 {
301 let mut req_body = disk_create::RequestBodyWithImage {
302 size,
303 image: image.into(),
304 root_pass: root_pass.into(),
305 ..Default::default()
306 };
307 f(&mut req_body);
308
309 let (_resp_status, resp_body, _resp_headers) = self
310 .respond::<(), _, _>(
311 Method::POST,
312 format!("/linode/instances/{}/disks", linode_id).as_str(),
313 None,
314 Some(&req_body),
315 )
316 .await?;
317 Ok(resp_body)
318 }
319
320 pub async fn linode_instances_disk_view(
321 &self,
322 linode_id: LinodeId,
323 disk_id: DiskId,
324 ) -> Result<disk_view::ResponseBody, V4ClientRespondError<C>> {
325 let (_resp_status, resp_body, _resp_headers) = self
326 .respond::<(), (), _>(
327 Method::GET,
328 format!("/linode/instances/{}/disks/{}", linode_id, disk_id).as_str(),
329 None,
330 None,
331 )
332 .await?;
333 Ok(resp_body)
334 }
335
336 pub async fn linode_types_types_list(
340 &self,
341 ) -> Result<types_list::ResponseBody, V4ClientRespondError<C>> {
342 let (_resp_status, resp_body, _resp_headers) = self
343 .respond::<(), (), types_list::ResponseBody>(
344 Method::GET,
345 "/linode/types",
346 None,
347 None,
348 )
349 .await?;
350 Ok(resp_body)
351 }
352 }
353}