1use 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
38pub type AccountRequest<M, V> = Request<M, V>;
40pub type ActionRequest<M, V> = Request<M, V>;
42pub type CertificateRequest<M, V> = Request<M, V>;
44pub type DomainRecordRequest<M, V> = Request<M, V>;
46pub type DomainRequest<M, V> = Request<M, V>;
48pub type DropletActionRequest<M, V> = Request<M, V>;
50pub type DropletRequest<M, V> = Request<M, V>;
52pub type FloatingIpActionRequest<M, V> = Request<M, V>;
54pub type FloatingIpRequest<M, V> = Request<M, V>;
56pub type ImageActionRequest<M, V> = Request<M, V>;
58pub type ImageRequest<M, V> = Request<M, V>;
60pub type CustomImageRequest<M, V> = Request<M, V>;
62pub type LoadBalancerRequest<M, V> = Request<M, V>;
64pub type RegionRequest<M, V> = Request<M, V>;
66pub type SizeRequest<M, V> = Request<M, V>;
68pub type SnapshotRequest<M, V> = Request<M, V>;
70pub type SshKeyRequest<M, V> = Request<M, V>;
72pub type TagRequest<M, V> = Request<M, V>;
74pub type VolumeActionRequest<M, V> = Request<M, V>;
76pub type VolumeRequest<M, V> = Request<M, V>;
78
79#[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    #[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    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    pub fn limit(mut self, limit: Option<usize>) -> Self {
125        self.method.0 = limit;
126        self
127    }
128}
129
130#[async_trait]
132pub trait Executable<T: HasResponse>: Sized {
133    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}