digitalocean_api/
request.rs

1//! Abstract types representing requests and how they are executed.
2//!
3//!
4
5use crate::api::{HasPagination, HasResponse};
6use crate::error::Error;
7use crate::method::{Create, Delete, Get, List, Method, Update};
8use crate::DigitalOcean;
9use async_trait::async_trait;
10use getset::{Getters, MutGetters, Setters};
11use serde::Deserialize;
12use serde_json::Value;
13use std::marker::PhantomData;
14use url::Url;
15
16mod url_serde {
17    #![allow(unused)]
18
19    use serde::{Deserialize, Deserializer, Serialize, Serializer};
20    use url::Url;
21
22    pub fn serialize<S>(url: &Url, serializer: S) -> Result<S::Ok, S::Error>
23    where
24        S: Serializer,
25    {
26        url.to_string().serialize(serializer)
27    }
28
29    pub fn deserialize<'de, D>(deserializer: D) -> Result<Url, D::Error>
30    where
31        D: Deserializer<'de>,
32    {
33        let url_str: String = String::deserialize(deserializer)?;
34        Url::parse(&url_str).map_err(serde::de::Error::custom)
35    }
36}
37
38/// A type alias with [`Request<_, Account>`](struct.Request.html) specific functions.
39pub type AccountRequest<M, V> = Request<M, V>;
40/// A type alias with [`Request<_, Action>`](struct.Request.html) specific functions.
41pub type ActionRequest<M, V> = Request<M, V>;
42/// A type alias with [`Request<_, Certificate>`](struct.Request.html) specific functions.
43pub type CertificateRequest<M, V> = Request<M, V>;
44/// A type alias with [`Request<_, DomainRecord>`](struct.Request.html) specific functions.
45pub type DomainRecordRequest<M, V> = Request<M, V>;
46/// A type alias with [`Request<_, Domain>`](struct.Request.html) specific functions.
47pub type DomainRequest<M, V> = Request<M, V>;
48/// A type alias with [`Request<_, DropletAction>`](struct.Request.html) specific functions.
49pub type DropletActionRequest<M, V> = Request<M, V>;
50/// A type alias with [`Request<_, Droplet>`](struct.Request.html) specific functions.
51pub type DropletRequest<M, V> = Request<M, V>;
52/// A type alias with [`Request<_, FloatingIpAction>`](struct.Request.html) specific functions.
53pub type FloatingIpActionRequest<M, V> = Request<M, V>;
54/// A type alias with [`Request<_, FloatingIp>`](struct.Request.html) specific functions.
55pub type FloatingIpRequest<M, V> = Request<M, V>;
56/// A type alias with [`Request<_, ImageAction>`](struct.Request.html) specific functions.
57pub type ImageActionRequest<M, V> = Request<M, V>;
58/// A type alias with [`Request<_, Image>`](struct.Request.html) specific functions.
59pub type ImageRequest<M, V> = Request<M, V>;
60/// A type alias with [`Request<_, CustomImage>`](struct.Request.html) specific functions.
61pub type CustomImageRequest<M, V> = Request<M, V>;
62/// A type alias with [`Request<_, LoadBalancer>`](struct.Request.html) specific functions.
63pub type LoadBalancerRequest<M, V> = Request<M, V>;
64/// A type alias with [`Request<_, Region>`](struct.Request.html) specific functions.
65pub type RegionRequest<M, V> = Request<M, V>;
66/// A type alias with [`Request<_, Size>`](struct.Request.html) specific functions.
67pub type SizeRequest<M, V> = Request<M, V>;
68/// A type alias with [`Request<_, Snapshot>`](struct.Request.html) specific functions.
69pub type SnapshotRequest<M, V> = Request<M, V>;
70/// A type alias with [`Request<_, SshKey>`](struct.Request.html) specific functions.
71pub type SshKeyRequest<M, V> = Request<M, V>;
72/// A type alias with [`Request<_, Tag>`](struct.Request.html) specific functions.
73pub type TagRequest<M, V> = Request<M, V>;
74/// A type alias with [`Request<_, VolumeAction>`](struct.Request.html) specific functions.
75pub type VolumeActionRequest<M, V> = Request<M, V>;
76/// A type alias with [`Request<_, Volume>`](struct.Request.html) specific functions.
77pub type VolumeRequest<M, V> = Request<M, V>;
78
79/// A consuming builder which can be used to build up API calls.
80///
81/// In general consumers of the crate should not need to use this type directly.
82/// Instead, build up requests from what is found in [`api::*`](../api/index.html).
83#[derive(Debug, Clone, Deserialize, MutGetters, Getters, Setters)]
84pub struct Request<A: Method, R> {
85    #[get_mut = "pub"]
86    #[set = "pub"]
87    #[get = "pub"]
88    #[serde(with = "url_serde")]
89    url: Url,
90
91    /// The JSON body of the request.
92    #[get_mut = "pub"]
93    #[set = "pub"]
94    #[get = "pub"]
95    body: Value,
96
97    #[get = "pub"]
98    method: A,
99
100    value: PhantomData<R>,
101}
102
103impl<A: Method, V> Request<A, V> {
104    /// Create a request pointing at the given url. `V` is the value ultimately
105    /// returned when the call is executed.
106    pub fn new(url: Url) -> Self {
107        Request {
108            url,
109            body: Value::Null,
110            method: A::default(),
111            value: PhantomData,
112        }
113    }
114
115    pub(crate) fn transmute<C: Method, D>(self) -> Request<C, D> {
116        let mut req = Request::new(self.url);
117        req.set_body(self.body);
118        req
119    }
120}
121
122impl<V> Request<List, V> {
123    /// Impose a limit on the number of values which may be retrieved from a request.
124    pub fn limit(mut self, limit: Option<usize>) -> Self {
125        self.method.0 = limit;
126        self
127    }
128}
129
130/// Describes an API call which can be executed.
131#[async_trait]
132pub trait Executable<T: HasResponse>: Sized {
133    /// Execute the corresponding call.
134    async fn execute(self, instance: &DigitalOcean) -> Result<T, Error>;
135}
136
137#[async_trait]
138impl<V> Executable<Vec<V>> for Request<List, Vec<V>>
139where
140    Vec<V>: HasResponse,
141    <Vec<V> as HasResponse>::Response: HasPagination,
142    V: std::marker::Send,
143{
144    async fn execute(self, instance: &DigitalOcean) -> Result<Vec<V>, Error> {
145        let response: Vec<V> = instance.list(self).await?;
146        Ok(response)
147    }
148}
149
150#[async_trait]
151impl<V: HasResponse + std::marker::Send> Executable<V> for Request<Create, V> {
152    async fn execute(self, instance: &DigitalOcean) -> Result<V, Error> {
153        let response = instance.post(self).await?;
154        Ok(response)
155    }
156}
157
158#[async_trait]
159impl<V: HasResponse + std::marker::Send> Executable<V> for Request<Update, V> {
160    async fn execute(self, instance: &DigitalOcean) -> Result<V, Error> {
161        let response = instance.put(self).await?;
162        Ok(response)
163    }
164}
165
166#[async_trait]
167impl<V: HasResponse + std::marker::Send> Executable<V> for Request<Get, V> {
168    async fn execute(self, instance: &DigitalOcean) -> Result<V, Error> {
169        let response = instance.get(self).await?;
170        Ok(response)
171    }
172}
173
174#[async_trait]
175impl Executable<()> for Request<Delete, ()> {
176    async fn execute(self, instance: &DigitalOcean) -> Result<(), Error> {
177        instance.delete(self).await
178    }
179}