linode_api/
v4_client.rs

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
13//
14pub 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//
91#[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        //
131        //
132        //
133        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        //
337        //
338        //
339        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}